├── index.js ├── consensus ├── index.js ├── README.md ├── message.js ├── pbft.js └── bi.js ├── boot.js ├── p2p ├── .travis.yml ├── node0.js ├── node1.js ├── node2.js ├── tests │ └── run.find_successor.js ├── .gitignore ├── LICENSE.md ├── package.json ├── index.js ├── libs │ ├── message.js │ ├── utils.js │ └── node.js └── README.md ├── peer.js ├── .travis.yml ├── nodes ├── index.js ├── boot.js └── peer.js ├── Makefile ├── envsetup ├── BOOT.env ├── PEER1.env └── PEER2.env ├── tests ├── build-merkle.js ├── run ├── suite │ ├── test.ChordDev.js │ ├── test.BootNode.js │ └── test.ChordUtils.js ├── get-local-ip.js └── node-rsa.js ├── libs └── index.js ├── config.js ├── utils ├── cli.js ├── verify.js ├── send.js └── Log.js ├── .editorconfig ├── .gitignore ├── package.json ├── LICENSE ├── HISTORY.md ├── block ├── genesis.js ├── block.js ├── difficulty.js └── mining.js ├── server ├── debug.js └── index.js ├── wot ├── router.js ├── requestHandlers.js ├── broker.js └── framework.js ├── CODE_OF_CONDUCT.md ├── database └── index.js └── README.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./nodes') 2 | -------------------------------------------------------------------------------- /consensus/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./pbft'); -------------------------------------------------------------------------------- /boot.js: -------------------------------------------------------------------------------- 1 | var BootNode = require('./index').BootNode; 2 | var node = new BootNode(); 3 | 4 | node.start(); 5 | -------------------------------------------------------------------------------- /p2p/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '4' 4 | after_success: ./node_modules/.bin/codecov 5 | -------------------------------------------------------------------------------- /peer.js: -------------------------------------------------------------------------------- 1 | var PeerNode = require('./index').PeerNode; 2 | var node = new PeerNode(); 3 | 4 | node.start(); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '8' 5 | after_success: ./node_modules/.bin/codecov 6 | -------------------------------------------------------------------------------- /nodes/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | BootNode: require('./boot'), 3 | PeerNode: require('./peer') 4 | }; 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = $(shell find tests/suite/test.*.js) 2 | 3 | test: 4 | @./tests/run $(TESTS) 5 | 6 | .PHONY: test 7 | -------------------------------------------------------------------------------- /envsetup/BOOT.env: -------------------------------------------------------------------------------- 1 | export HOST=172.20.10.3 2 | export PORT=8000 3 | export DEBUGSERVER=172.20.10.3 4 | export DEBUGSERVER_PORT=5000 5 | -------------------------------------------------------------------------------- /envsetup/PEER1.env: -------------------------------------------------------------------------------- 1 | export HOST=172.20.10.3 2 | export PORT=9002 3 | export PEER_ADDR=172.20.10.3 4 | export PEER_PORT=8000 5 | export DEBUGSERVER=172.20.10.3 6 | export DEBUGSERVER_PORT=5000 7 | -------------------------------------------------------------------------------- /envsetup/PEER2.env: -------------------------------------------------------------------------------- 1 | export HOST=172.20.10.3 2 | export PORT=9001 3 | export PEER_ADDR=172.20.10.3 4 | export PEER_PORT=8000 5 | export DEBUGSERVER=172.20.10.3 6 | export DEBUGSERVER_PORT=5000 7 | -------------------------------------------------------------------------------- /p2p/node0.js: -------------------------------------------------------------------------------- 1 | var server = require('./libs/server'); 2 | 3 | /** 4 | * Chord network. 5 | */ 6 | var onmessage = function(payload) { 7 | }; 8 | 9 | /** 10 | * Create a virtual node (seed node). 11 | */ 12 | server.start({ 13 | onmessage: onmessage, 14 | }); 15 | -------------------------------------------------------------------------------- /p2p/node1.js: -------------------------------------------------------------------------------- 1 | var server = require('./libs/server'); 2 | 3 | /** 4 | * Chord network. 5 | */ 6 | var onmessage = function(payload) { 7 | }; 8 | 9 | /** 10 | * Join an existing node. 11 | */ 12 | server.start({ 13 | onmessage: onmessage, 14 | join: { 15 | address: 'localhost', 16 | port: 8000 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /p2p/node2.js: -------------------------------------------------------------------------------- 1 | var server = require('./libs/server'); 2 | 3 | /** 4 | * Chord network. 5 | */ 6 | var onmessage = function(payload) { 7 | }; 8 | 9 | /** 10 | * Join an existing node. 11 | */ 12 | server.start({ 13 | onmessage: onmessage, 14 | join: { 15 | address: 'localhost', 16 | port: 8000 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /tests/build-merkle.js: -------------------------------------------------------------------------------- 1 | var merkle = require('merkle'); 2 | var merkleRoot = merkle('sha256'); 3 | 4 | // 建立一筆新的交易紀錄 5 | var tx = ['a', 'b', 'c', 'd']; 6 | 7 | merkleRoot.async(tx, function(err, tree){ 8 | // 印出所有節點 9 | for (i = 0; i < tree.levels(); i++) { 10 | console.log( tree.level(i) ); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /libs/index.js: -------------------------------------------------------------------------------- 1 | // Import Websocket server 2 | var WoTServer = require('../server'); 3 | 4 | // Crypto 5 | var Crypto = require('crypto'); 6 | 7 | // Database 8 | var DatabaseAdapter = require('../database'); 9 | 10 | module.exports = { 11 | WoTServer: WoTServer, 12 | Crypto: Crypto, 13 | DatabaseAdapter: DatabaseAdapter 14 | } 15 | -------------------------------------------------------------------------------- /tests/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export NODE_ENV=test 4 | 5 | echo 6 | for file in $@; do 7 | printf "\033[90m ${file#test/}\033[0m " 8 | ./node_modules/.bin/vows $file --spec 2> /tmp/stderr && echo "\033[36m✓\033[0m" 9 | code=$? 10 | if test $code -ne 0; then 11 | echo "\033[31m✖\033[0m" 12 | cat /tmp/stderr >&2 13 | exit $code 14 | fi 15 | done 16 | echo 17 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.genesis = { 4 | no: 0, 5 | 6 | hash: 'dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6', 7 | 8 | previousHash: '0000000000000000000000000000000000000000000000000000000000000000', 9 | 10 | difficulty: '0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 11 | 12 | nonce: 0, 13 | 14 | timestamp: new Date(), 15 | 16 | merkleRoot: {} 17 | }; 18 | -------------------------------------------------------------------------------- /tests/suite/test.ChordDev.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | ChordUtils = require('../../p2p/libs/utils'); 4 | 5 | vows.describe('Chord Development Environment').addBatch({ 6 | 'Testing hashTestId()': { 7 | 'is 4-bytes length': function () { 8 | var id = ChordUtils.hashTestId(); 9 | 10 | assert.strictEqual(id.length, 4); 11 | } 12 | } 13 | }).export(module);; 14 | -------------------------------------------------------------------------------- /utils/cli.js: -------------------------------------------------------------------------------- 1 | // Import Websocket server 2 | var PeerNode = require('../index').PeerNode; 3 | var node = new PeerNode(); 4 | 5 | // Application event callbacks 6 | var ondata = function(req, res) { 7 | var data = req.data; 8 | 9 | console.log('[CLI] verifying key =', data.key); 10 | res.read(data.key); 11 | }; 12 | 13 | // Start the server 14 | node.start({ 15 | ondata: ondata, 16 | join: { 17 | address: 'localhost', 18 | port: '8000' 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.js] 13 | ; CodePainter extended properties 14 | quote_type = auto 15 | spaces_around_operators = true 16 | space_after_control_statements = true 17 | space_after_anonymous_functions = false 18 | spaces_in_brackets = false 19 | 20 | [*.jade] 21 | indent_size = 2 22 | 23 | [*.md] 24 | trim_trailing_whitespace = false 25 | 26 | [*.sass] 27 | indent_size = 2 28 | 29 | [node_modules/**.js] 30 | codepaint = false 31 | -------------------------------------------------------------------------------- /tests/get-local-ip.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var ifaces = os.networkInterfaces(); 5 | 6 | Object.keys(ifaces).forEach(function (ifname) { 7 | var alias = 0; 8 | 9 | ifaces[ifname].forEach(function (iface) { 10 | if ('IPv4' !== iface.family || iface.internal !== false) { 11 | // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses 12 | return; 13 | } 14 | 15 | if (alias >= 1) { 16 | // this single interface has multiple ipv4 addresses 17 | console.log(ifname + ':' + alias, iface.address); 18 | } else { 19 | // this interface has only one ipv4 adress 20 | console.log(ifname, iface.address); 21 | } 22 | ++alias; 23 | }); 24 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.swp 11 | *.DS_Store 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | node_modules 30 | 31 | # Optional npm cache directory 32 | .npm 33 | 34 | # Optional REPL history 35 | .node_repl_history 36 | 37 | # Babel compiled files 38 | src/*.js 39 | src/Components/*.js 40 | 41 | # LevelDB 42 | mydb* 43 | -------------------------------------------------------------------------------- /consensus/README.md: -------------------------------------------------------------------------------- 1 | # Flowchain Consensus Algorithm 2 | 3 | ## Practical BFT (PBFT) 4 | 5 | The PBFT implementation. 6 | 7 | ## Brooks–Iyengar algorithm 8 | 9 | The sensor network needs a fault tolerant algorithm. Therefore, Flowchain adopts the Brooks–Iyengar algorithm for the distributed sensor network. The Brooks–Iyengar is a distributed fault-tolerant algorithm which the precision and accuracy bound have been proved in 2016.[1] 10 | 11 | In the Flowchain p2p network, each node performs the algorithm separately to exchange the measured value and accuracy value. 12 | 13 | 1. Ao, Buke; Wang, Yongcai; Yu, Lu; Brooks, Richard R.; Iyengar, S. S. (2016-05-01). "On Precision Bound of Distributed Fault-Tolerant Sensor Fusion Algorithms". ACM Comput. Surv. 49 (1): 5:1–5:23. ISSN 0360-0300. doi:10.1145/2898984. 14 | Jump up ^ 15 | -------------------------------------------------------------------------------- /p2p/tests/run.find_successor.js: -------------------------------------------------------------------------------- 1 | var to = { address: '127.0.0.1', port: 8000 }; 2 | var message = { type: 2, id: '2e9c3bbeb0827d26dd121d014fa34e73' }; 3 | 4 | var util = require('util'); 5 | var WebSocketClient = require('websocket').client; 6 | var hash = require('../libs/utils').hash; 7 | var uuid = require('uuid'); 8 | 9 | var client = new WebSocketClient(); 10 | 11 | client.on('connect', function(connection) { 12 | var payload = { 13 | to: to.id, 14 | message: message, 15 | from: { 16 | address: '127.0.0.1', 17 | port: 8001, 18 | id: message.id 19 | } 20 | }; 21 | 22 | if (connection.connected) { 23 | connection.sendUTF(JSON.stringify(payload)); 24 | } 25 | 26 | process.exit(0); 27 | }); 28 | 29 | var uri = util.format('ws://%s:%s/node/%s/receive', to.address, to.port, message.id) 30 | 31 | console.log(uri); 32 | client.connect(uri, ''); 33 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flowchain.js", 3 | "version": "0.5.0", 4 | "dependencies": { 5 | "backbone": "^1.3.3", 6 | "big-integer": "^1.6.40", 7 | "chalk": "^2.4.1", 8 | "codecov": "^3.1.0", 9 | "console.table": "^0.8.0", 10 | "diff": "^4.0.1", 11 | "flowchain-hybrid": "*", 12 | "gaussian": "^1.1.0", 13 | "ipfs-api": "^26.1.2", 14 | "leveldown": "^4.0.1", 15 | "levelup": "^1.3.3", 16 | "merkle": "^0.5.1", 17 | "moment": "^2.22.2", 18 | "nedb": "^1.8.0", 19 | "node-rsa": "^0.4.2", 20 | "picodb": "^0.8.1", 21 | "underscore": "^1.12.1", 22 | "utils-merge": "^1.0.0", 23 | "uuid": "^3.0.1", 24 | "vows": "^0.8.1", 25 | "websocket": "^1.0.23" 26 | }, 27 | "scripts": { 28 | "test": "make test" 29 | }, 30 | "devDependencies": {}, 31 | "author": "jollen", 32 | "engines": { 33 | "node": "8.14" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /p2p/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.swp 11 | *.DS_Store 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | node_modules 30 | 31 | # Optional npm cache directory 32 | .npm 33 | 34 | # Optional REPL history 35 | .node_repl_history 36 | 37 | # Configuration files 38 | components/configs/*/*.conf 39 | 40 | # Config files 41 | config.*.json 42 | config.json 43 | config*json 44 | 45 | # Templates 46 | templates/*/.git 47 | templates/*/.gitignore 48 | templates/*/iisnode.yml 49 | -------------------------------------------------------------------------------- /tests/suite/test.BootNode.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'); 3 | 4 | var Flowchain = require('../../libs'); 5 | 6 | // Import Websocket server 7 | var server = Flowchain.WoTServer; 8 | 9 | // Utils 10 | var crypto = Flowchain.Crypto; 11 | 12 | // Database 13 | var Database = Flowchain.DatabaseAdapter; 14 | 15 | vows.describe('Chord Development Environment').addBatch({ 16 | 'Testing Boot Node': { 17 | 'is server started': function () { 18 | 19 | var onstart = function(req, res) { 20 | // Chord node ID 21 | var id = req.node.id; 22 | var address = req.node.address; 23 | 24 | assert.strictEqual(id.length, 40); 25 | 26 | server.shutdown(function() { 27 | process.exit(0); 28 | }); 29 | }; 30 | 31 | server.start({ 32 | onstart: onstart 33 | }); 34 | } 35 | } 36 | }).export(module);; 37 | -------------------------------------------------------------------------------- /p2p/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jollen Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /utils/verify.js: -------------------------------------------------------------------------------- 1 | var WebSocketClient = require('websocket').client; 2 | var key = process.argv[2]; 3 | var client = new WebSocketClient(); 4 | 5 | client.on('connectFailed', function(error) { 6 | console.log('Connect Error: ' + error.toString()); 7 | }); 8 | 9 | client.on('connect', function(connection) { 10 | console.log('WebSocket client connected'); 11 | connection.on('error', function(error) { 12 | console.log("Connection Error: " + error.toString()); 13 | }); 14 | connection.on('close', function() { 15 | console.log('echo-protocol Connection Closed'); 16 | }); 17 | connection.on('message', function(message) { 18 | if (message.type === 'utf8') { 19 | console.log("Received: '" + message.utf8Data + "'"); 20 | } 21 | }); 22 | 23 | if (connection.connected) { 24 | var obj = { key: key }; 25 | 26 | console.log('Verifying... ' + JSON.stringify(obj)); 27 | 28 | connection.sendUTF(JSON.stringify(obj)); 29 | } 30 | }); 31 | 32 | client.connect('ws://localhost:8000/object/5550937980d51931b3000009/send', ''); 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-present Jollen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | ## Beta Stage 4 | 5 | beta-v0.2: 2018.03.23 6 | * Prepare for the "v1.0" 7 | * Node.js 0.10 ready for ARM9TDMI microprocessors 8 | * Node.js 0.12 ready for MIPS microprocessors 9 | * Node.js v6+ ready for laptops, desktops and servers 10 | 11 | ## Alpha Stage 12 | 13 | alpha-v0.6: 2017.03.07 14 | * Add PicoDB support 15 | * Support MediaTek LinkIt Smart 7688 16 | * Pre-release for public alpha 17 | 18 | alpha-v0.5: 2016.12.28 19 | * Support transaction verify 20 | * Update boot node and peer node applications 21 | 22 | alpha-v0.4: 2016.12.28 23 | * Bug fixes. ([#62bbfb876f0cca1c4144cc55831a2a5cd42e4f6d]) 24 | * Support leveldb and nedb 25 | * Add new event: ```on data``` 26 | 27 | alpha-v0.3: 2016.12.27 28 | * New difficulty algorithm based on normal distribution 29 | * Support [IoT broker architecture](https://wotcity.com) 30 | * Support data query 31 | * Support failure check 32 | 33 | alpha-v0.2: 2016.12.26 34 | * New feature: Save K to successor(K) 35 | * New feature: event aggregation 36 | * Support block store based on LevelDB 37 | 38 | alpha-v0.1: 2016.12.23 39 | * Ontology: blockchain, IoT, WoT, p2p and IoT hub/gateway. 40 | * Architecture: REST, RPC 41 | -------------------------------------------------------------------------------- /p2p/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-p2p-chord", 3 | "version": "0.5.2", 4 | "description": "A light-weight Chord protocol implementation for a peer-to-peer distributed hash table.", 5 | "keywords": [ 6 | "chord", 7 | "p2p" 8 | ], 9 | "author": "jollen (http://jollen.org/)", 10 | "main": "libs/server.js", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/jollen/node-p2p-chord.git" 14 | }, 15 | "homepage": "https://github.com/jollen/node-p2p-chord.git", 16 | "dependencies": { 17 | "assert": "^1.4.1", 18 | "backbone": "^1.3.3", 19 | "bl": "^0.9.4", 20 | "bluebird": "^3.3.3", 21 | "coap": "^0.10.3", 22 | "codecov": "^1.0.1", 23 | "console.table": "^0.7.0", 24 | "underscore": "^1.8.3", 25 | "util": "^0.10.3", 26 | "utils-merge": "^1.0.0", 27 | "uuid": "^2.0.2", 28 | "vows": "^0.8.1", 29 | "websocket": "^1.0.17", 30 | "wotcity.io": "*" 31 | }, 32 | "scripts": { 33 | "test": "./node_modules/.bin/vows tests/tests.js --spec", 34 | "start": "node ./websocket-broker-server.js" 35 | }, 36 | "devDependencies": { 37 | "codecov": "^1.0.1", 38 | "mocha": "^2.4.5", 39 | "twilio": "^2.9.0" 40 | }, 41 | "license": "MIT License" 42 | } 43 | -------------------------------------------------------------------------------- /utils/send.js: -------------------------------------------------------------------------------- 1 | var WebSocketClient = require('websocket').client; 2 | 3 | var client = new WebSocketClient(); 4 | 5 | client.on('connectFailed', function(error) { 6 | console.log('Connect Error: ' + error.toString()); 7 | }); 8 | 9 | client.on('connect', function(connection) { 10 | console.log('WebSocket client connected'); 11 | connection.on('error', function(error) { 12 | console.log("Connection Error: " + error.toString()); 13 | }); 14 | connection.on('close', function() { 15 | console.log('echo-protocol Connection Closed'); 16 | }); 17 | connection.on('message', function(message) { 18 | if (message.type === 'utf8') { 19 | console.log("Received: '" + message.utf8Data + "'"); 20 | } 21 | }); 22 | 23 | function sendNumber() { 24 | if (connection.connected) { 25 | var number = Math.round(Math.random() * 0xFFFFFF); 26 | var lucky = Math.round(Math.random() * 100 + 1); 27 | var obj = {temperature: lucky}; 28 | 29 | console.log('[SEND]', JSON.stringify(obj)); 30 | 31 | connection.sendUTF(JSON.stringify(obj)); 32 | setTimeout(sendNumber, 100); 33 | } 34 | } 35 | sendNumber(); 36 | }); 37 | 38 | client.connect('ws://localhost:8000/object/frontdoor/send', ''); 39 | -------------------------------------------------------------------------------- /p2p/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | 32 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 33 | module.exports = require('./libs/node'); 34 | -------------------------------------------------------------------------------- /block/genesis.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.io 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var config = require('../config.js'); 32 | var Block = require('./block'); 33 | 34 | var genesis = new Block(config.genesis); 35 | 36 | module.exports = genesis; 37 | -------------------------------------------------------------------------------- /server/debug.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var Debug = { 32 | Verbose: false, 33 | }; 34 | 35 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 36 | module.exports = Debug; 37 | -------------------------------------------------------------------------------- /consensus/message.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2017-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | /** 32 | * The RPC operations built upon Chord protocols. 33 | */ 34 | var PBFT = { 35 | INIT: 0, 36 | PREPARE: 2, 37 | COMMIT: 3 38 | }; 39 | 40 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 41 | module.exports = PBFT; 42 | -------------------------------------------------------------------------------- /block/block.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.io 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | function Block(block) { 32 | if (typeof block === 'undefined') { 33 | block = {}; 34 | } 35 | 36 | this.hash = block.hash || ''; 37 | this.previousHash = block.previousHash || ''; 38 | this.timestamp = block.timestamp || new Date(); 39 | this.merkleRoot = block.merkleRoot || '0000000000000000000000000000000000000000000000000000000000000000'; 40 | this.difficulty = block.difficulty || '0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'; 41 | this.nonce = block.nonce || 0; 42 | this.no = block.no < 0 ? 0 : block.no; 43 | } 44 | 45 | module.exports = Block; 46 | -------------------------------------------------------------------------------- /p2p/libs/message.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | /** 32 | * The RPC operations built upon Chord protocols. 33 | */ 34 | var Chord = { 35 | NOTIFY_STABILIZE: 0, 36 | NOTIFY_PREDECESSOR: 1, 37 | NOTIFY_SUCCESSOR: 2, 38 | NOTIFY_JOIN: 3, 39 | 40 | // Send messages to the hybrid node (aka. the edge node) for consensus 41 | NOTIFY_EDGE: 4, 42 | 43 | FIND_SUCCESSOR: 5, 44 | FOUND_SUCCESSOR: 6, 45 | CHECK_PREDECESSOR: 7, 46 | CHECK_SUCESSOR: 8, 47 | CHECK_TTL: 9, 48 | MESSAGE: 10 49 | }; 50 | 51 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 52 | module.exports = Chord; 53 | -------------------------------------------------------------------------------- /utils/Log.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | var chalk = require('chalk'); 3 | var fs = require('fs'); 4 | var util = require('util'); 5 | 6 | var config = JSON.parse( fs.readFileSync(__dirname + '/../package.json') ); 7 | 8 | /** 9 | * Log utils 10 | */ 11 | var TAG = 'Flowchain/IPFS'; 12 | 13 | var getTimeStamp = function(tag) { 14 | var ts = moment().toISOString(); 15 | var _ts = ts.split(/[T:\.Z]/); // [ '2018-06-24', '03', '55', '14', '303', '' ] 16 | 17 | return (chalk.grey('[') + chalk.green(tag + '') + ' ' 18 | + chalk.red( 19 | _ts[1] + ':' + 20 | _ts[2] + ':' + 21 | _ts[3]) 22 | + chalk.grey(']')); 23 | }; 24 | 25 | var spread_join = function(msg, ...argv) { 26 | var result; 27 | 28 | if (typeof msg === 'string') { 29 | result = msg; 30 | } 31 | 32 | if (typeof argv === 'object') { 33 | result = result + ' ' + argv.join(' '); 34 | } 35 | 36 | return result; 37 | }; 38 | 39 | var LOGI = function(tag = TAG, msg, ...argv) { 40 | console.log(getTimeStamp(tag), spread_join(msg, argv)); 41 | }; 42 | 43 | var LOGE = function(tag = TAG, msg, ...argv) { 44 | console.log(getTimeStamp('Error'), spread_join(tag + '/' + msg, argv)); 45 | }; 46 | 47 | var LOGV = function(tag = TAG, msg, ...argv) { 48 | console.log(getTimeStamp('Verbose'), spread_join(tag + '/' + msg, argv)); 49 | }; 50 | 51 | console.info = function(tag = TAG, msg, ...argv) { 52 | if (typeof msg === 'undefined') { 53 | msg = tag; 54 | tag = util.format('%s %s', config.name, config.version); 55 | } 56 | 57 | return LOGI(tag, msg, ...argv); 58 | }; 59 | 60 | console.debug = function(tag = TAG, msg, ...argv) { 61 | if (typeof msg === 'undefined') { 62 | msg = tag; 63 | tag = util.format('%s %s', config.name, config.version); 64 | } 65 | 66 | return LOGV(tag, msg, ...argv); 67 | }; 68 | 69 | module.exports = { 70 | i: LOGI, 71 | e: LOGE, 72 | v: LOGV 73 | }; 74 | -------------------------------------------------------------------------------- /consensus/pbft.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.co 6 | * 7 | * Copyright (c) 2017-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | var merge = require('utils-merge'); 30 | 31 | // The PBFT protocols 32 | var PBFT_PROTOCOLS = require('./message'); 33 | 34 | function PBFT(options) { 35 | }; 36 | 37 | PBFT.prototype.broadcast = function(message) { 38 | }; 39 | 40 | /** 41 | * 42 | * @param {Function} Complete callback function 43 | * @api private 44 | */ 45 | PBFT.prototype.dispatch = function(_from, _message) { 46 | var from = _from; 47 | var message = _message; 48 | 49 | switch (message.type) { 50 | case PBFT_PROTOCOLS.INIT: 51 | case PBFT_PROTOCOLS.PREPARE: 52 | message.type = PBFT_PROTOCOLS.COMMIT; 53 | this.broadcast(message); 54 | break; 55 | case PBFT_PROTOCOLS.COMMIT: 56 | } 57 | }; 58 | 59 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 60 | module.exports = PBFT; 61 | }; 62 | -------------------------------------------------------------------------------- /tests/node-rsa.js: -------------------------------------------------------------------------------- 1 | var NodeRSA = require('node-rsa'); 2 | 3 | /* 4 | * Load key from PEM string 5 | */ 6 | var keyData = 7 | '-----BEGIN RSA PRIVATE KEY-----\n'+ 8 | 'MIIEpQIBAAKCAQEAvHBt1149/oWUZT9ji7NLd8nWb1zFnQ1p9gz5zoZtxyJ0iKuQ\n'+ 9 | 'rMUl7Yw93r4+TpPwXBBn0aeZo5EAaN+zg0VDkM+bKujTezTdJRmg9TkycAhyWjPI\n'+ 10 | 'UdsJRwu5ivXkYKzYAujzySKlVfnm3E8i3jafy0s8ezpzeVNR0741HkG6ft16ajMl\n'+ 11 | 'wOfizay8w/z9mwJ8HxTqs4Ehhkm8NJ6zCLr3haCzHbKfQpzYXi9ZTocRKpKtZYfk\n'+ 12 | 'OngPJlIlJTDxL32/LTq6A1nHUiNCIsxQQ6ArfZocbTEcUuMQd2xTLsio+bX897Rp\n'+ 13 | 'D0f6s4y/t2HC4bf0DyI10/M5B/bdkykrY4QwxQIDAQABAoIBAA667ystt8TkpkP/\n'+ 14 | '34U1H8oeYf/UjbIIMWdFfmWRhpVRcTCZ0LZgtuVJ2DBISNCVqe1YbvUpl838cL7B\n'+ 15 | '9eNmD7ELOYLyLk3sRPk4dqeUsAen8WBxudAEQkZmeTuOmyqfeEdJ9Hydk7UT0uMX\n'+ 16 | '4I7kUDtxBypO2GX8iGH39WkHFFAEV83CCDD19jF7hVk4Fj5celosFWQUcUNIrE51\n'+ 17 | 'tUKJGt1zhGtsqhHotKAc64QdHrgV6ue0b7XVs5G/PF39vROYqD6GFT+nUkFEqRZz\n'+ 18 | 'fglQbr8PaibdMWxK/dz6dYcIz5FLygY2GnpkQdsF5dir9rX6qypaIihlwAMhF+ij\n'+ 19 | 'Nj+o7AUCgYEA28AVdi2gN4XSuKpwqJYnNqyw/KMOpG7nZBD9Wc+P0FjPyZHVQrxX\n'+ 20 | 'vhu4MFV9zdupLaFGP6VpSAavq2Bct5tGnzB99T3fmfzMNfJeoDknGzdZekxetkyO\n'+ 21 | 'oBpHH15Q3fKemVs7K6PyaWqDyN0lsDBEUF/ZWvioUxNRSILQ5yaC+yMCgYEA24YY\n'+ 22 | 'ZDhGj9/geH6d/6WGvDHbO1xcbzaRr+GReMrqOJZI/5YawC/7Ua+smOfQgSlm+d0s\n'+ 23 | 'vAFGE0GsqfqxMSrKWL61FaMeb3cTEdSNpQHN9fcXPzjj+VBqaxGGBTeKZu5WiT05\n'+ 24 | 'F9EJsxn5lG6B3O1ylTExiwbfJBNGCU8m5rMTtvcCgYEAh9b1bVhG8guHdx/lBFHN\n'+ 25 | 'a77UqVcidgMYhoL6Gcp03BYKXFAJxHcoxhvcgARZACgJLGvFQRK/QgbgENBAgD88\n'+ 26 | 'KKuRMUhOMKJUmgR4+hJaWjic1zzeT1KD1/Rmgr2Kv6h64dHDgfaWoxN043XRFli/\n'+ 27 | '4e9eLR4I71HBrVEwUbM8xIECgYEAupf+v6E+CSlIyeMGNbjjD7BpIsndIAMITrGz\n'+ 28 | 'TzNbZ4IGxok2b8nZG5PxZ38TsdYZW6VuUtfaUp/uPgWC+8HdgRWJIr0mL6TNJsi/\n'+ 29 | 'JVSlVr8SmYCn9tEtw5h/jIurLtbD45+QmffrALvBczODWuDSFpJcBEpw/V8Mlvka\n'+ 30 | 'ndn1lf0CgYEAxvTLQGoVblZOmkL8d/lILdO6KNk+EWHXmSYi2BsyPZ7JHGoXOPZF\n'+ 31 | 'sMKGylniYig2vi0tHrkR0c2yA9+81/S++Y5ODiSVlDiOc0NQEvw0mFbxregFkRxV\n'+ 32 | 'd+HEPKioC9aIW3+nykW7vKOz1Ezfxz7TgTrSWukXX5MwyjtP4SUsW4s=\n'+ 33 | '-----END RSA PRIVATE KEY-----'; 34 | //var key = new NodeRSA(); 35 | //key.importKey(keyData, 'pkcs1'); 36 | 37 | /* 38 | * Generate a new 512bit-length key (64 bytes) 39 | */ 40 | var key = new NodeRSA({b: 512}); 41 | key.importKey(keyData, 'pkcs1'); 42 | 43 | var newPrivateKey = key.exportKey('pkcs1-der') 44 | var encrypted = key.encrypt(newPrivateKey, 'base64'); 45 | 46 | console.log( encrypted ); 47 | -------------------------------------------------------------------------------- /wot/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | /** 32 | * Module dependencies. 33 | */ 34 | 35 | 36 | /** 37 | * Expose `WebsocketRouter` constructor. 38 | */ 39 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 40 | exports = module.exports = WebsocketRouter; 41 | 42 | /** 43 | * Initialize a new `WebsocketBroker` with the given `options`. 44 | * 45 | * @param {Object} options 46 | * @api private 47 | */ 48 | 49 | function WebsocketRouter(options) { 50 | options = options || {}; 51 | this.clientsPath = []; 52 | this.host = options.host ? options.host : 'localhost'; 53 | this.port = options.port ? options.port : 8000; 54 | } 55 | 56 | /** 57 | * Initialize a new `WebsocketBroker` with the given `options`. 58 | * 59 | * @param {Object} options 60 | * @api private 61 | */ 62 | 63 | var pathToRegExp = function(path) { 64 | if (typeof(path) === 'string') { 65 | if (path === '*') { 66 | path = /^.*$/; 67 | } 68 | else { 69 | //path = path.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); 70 | path = new RegExp('^' + path + '$'); 71 | } 72 | } 73 | return path; 74 | }; 75 | 76 | /** 77 | * Initialize a new `WebsocketBroker` with the given `options`. 78 | * 79 | * @param {Object} options 80 | * @api putblic 81 | */ 82 | 83 | WebsocketRouter.prototype.route = function(pathname, connection, wsHandlers, clients) { 84 | for(var path in wsHandlers) { 85 | var handler = wsHandlers[path]; 86 | var pathExp = pathToRegExp(path); 87 | 88 | if (!(pathExp instanceof RegExp)) { 89 | throw new Error('Path must be specified as either a string or a RegExp.'); 90 | } 91 | 92 | if (typeof handler === 'function') { 93 | if (pathExp.test(pathname)) { 94 | wsHandlers[path](pathname, connection, clients); 95 | } 96 | } else { 97 | console.info('no request handler for ' + pathname); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /p2p/README.md: -------------------------------------------------------------------------------- 1 | # node-p2p-chord 2 | 3 | [![](https://travis-ci.org/jollen/node-p2p-chord.svg?branch=master)](https://travis-ci.org/jollen/node-p2p-chord) 4 | 5 | *node-p2p-chord* is a light-weight Chord protocol and algorithm implementation in Node.js for a peer-to-peer distributed hash table over WebSocket. It's a side project of [flowchain](https://github.com/flowchain). 6 | 7 | This project aims to help building the P2P IoT networks in more simplicity way. 8 | 9 | ## Usage 10 | 11 | To start a virtual node. 12 | 13 | ``` 14 | $ export HOST=192.168.0.3 ; the IP address for this Chord node to listening to 15 | $ export PORT=8000 ; the port number for this Chord node to listening to 16 | $ node node0.js ; start the the virtual node 17 | ``` 18 | 19 | To join a existing node. 20 | 21 | ``` 22 | $ export HOST=192.168.0.100 ; the IP address for this Chord node to listening to 23 | $ export PORT=9000 ; the port number for this Chord node to listening to 24 | $ node node1.js ; start a Chord node and join the existing node 25 | ``` 26 | 27 | In ```node1.js```, you must add ```join``` to join a node. 28 | 29 | ``` 30 | // to connect to a subsequent node 31 | server.start({ 32 | onmessage: onmessage, 33 | join: { 34 | address: '192.168.0.3', 35 | port: 8000 36 | } 37 | }); 38 | ``` 39 | 40 | ## Quickstart 41 | 42 | To create a node and connect to a subsequent node. Add ```join``` as the existing server to connect to. 43 | 44 | ``` 45 | var server = require('./libs/server'); 46 | 47 | /** 48 | * Chord network. 49 | */ 50 | var onmessage = function(payload) { 51 | }; 52 | 53 | /** 54 | * Join an existing node. 55 | */ 56 | server.start({ 57 | onmessage: onmessage, 58 | join: { 59 | address: '127.0.0.1', 60 | port: 8001 61 | } 62 | }); 63 | ``` 64 | 65 | To create a new virtual node. 66 | 67 | ``` 68 | var server = require('./libs/server'); 69 | 70 | /** 71 | * Chord network. 72 | */ 73 | var onmessage = function(payload) { 74 | }; 75 | 76 | /** 77 | * Create a virtual node (seed node). 78 | */ 79 | server.start({ 80 | onmessage: onmessage, 81 | }); 82 | ``` 83 | 84 | ## History 85 | 86 | v0.5: current 87 | * Major bug fixed: [#0ee56e413a8c8bab68da282a445f5705fc34305b], [c7db7fdfc731c6f103f955b763c1f80862ff66c8], [#4c2108c7700f4211d4e6d1c3cc45afedbb4ee2f3], [#8e7d882b23ebff9fbcd3599bac09a4ac2dbf9f8b] 88 | * Support node stabilization over RPC, [#4e05e91f54a0dae8a179f4ae869fe171cf1734e4] 89 | 90 | v0.4: 2016.12.19 91 | * Major bug fixed 92 | * FOUND_SUCCESSOR bugs 93 | * NOTIFY_SUCCESSOR bugs 94 | * Improve startUpdateFingers() 95 | * Add a new ```NOTIFY_JOIN``` RPC message 96 | 97 | v0.3: 2016.08.27 98 | * Support refreshing finger table entries 99 | * Support verifying successor's consistency 100 | 101 | v0.2: 2016.08.26 102 | * Support create and join 103 | * Support stabilize and notify 104 | 105 | v0.1: 2016.08.25 106 | * Chord node prototype 107 | * Chord node over WebSocket server 108 | 109 | ## Credits 110 | 111 | There are existing Node.js Chord implementations. And *node-p2p-chord* is inspired by them. 112 | 113 | * [shigasumi/chord-node-js](https://github.com/shigasumi/chord-node-js) 114 | * [optimizely/chord](https://github.com/optimizely/chord) 115 | * [tsujio/webrtc-chord](https://github.com/tsujio/webrtc-chord) 116 | 117 | ## License 118 | 119 | The MIT License (MIT) 120 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jollen@flowchain.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /consensus/bi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.co 6 | * 7 | * Copyright (c) 2017-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | var merge = require('utils-merge'); 30 | 31 | // The PBFT protocols 32 | var PBFT_PROTOCOLS = require('./message'); 33 | 34 | var INTERVAL_SIZE = 5; 35 | 36 | function BI(options) { 37 | // initialize the private attributes 38 | this.options = { 39 | interval: 1000, /* measurement time interval in ms */ 40 | faulty: 0, /* the number of faulty sensors */ 41 | local_measurements: [], /* local interval measurements */ 42 | data_id: 0, /* the data ID */ 43 | }; 44 | 45 | // copy the attributes 46 | if (typeof(options) === 'object') { 47 | for (var prop in options) { 48 | if(options.hasOwnProperty(prop)) 49 | this.options[prop] = options[prop]; 50 | } 51 | } 52 | 53 | return this; 54 | }; 55 | 56 | /** 57 | * Get a measurement value over time interval 58 | * 59 | * @param {Function} Complete callback function 60 | * @api private 61 | */ 62 | BI.prototype.getMeasure = function (cb) { 63 | setInterval(function get_measurement_over_time_interval() { 64 | // generate the *sorted* simulating sensor data 65 | for (var i = 0; i < INTERVAL_SIZE; i++) { 66 | this.local_measurements[i] = Math.floor((Math.random() + i) * 100) / 100; 67 | } 68 | if (typeof(cb) === 'function') 69 | cb(this.local_measurements); 70 | }.bind(this), this.interval); 71 | }; 72 | 73 | /** 74 | * Fuse all of the sensor data 75 | * 76 | * @api private 77 | */ 78 | BI.prototype.fuse = function (cb) { 79 | var min = this.local_measurements[0].data; 80 | var max = this.local_measurements[INTERVAL_SIZE - 1].data; 81 | }; 82 | 83 | /** 84 | * 85 | * @param {Function} Complete callback function 86 | * @api private 87 | */ 88 | PBFT.prototype.dispatch = function(_from, _message) { 89 | var from = _from; 90 | var message = _message; 91 | 92 | switch (message.type) { 93 | case PBFT_PROTOCOLS.INIT: 94 | case PBFT_PROTOCOLS.PREPARE: 95 | message.type = PBFT_PROTOCOLS.COMMIT; 96 | this.node.broadcast(message); 97 | break; 98 | case PBFT_PROTOCOLS.COMMIT: 99 | } 100 | }; 101 | 102 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 103 | module.exports = { 104 | // Brooks–Iyengar algorithm (BI) 105 | BI: BI 106 | }; 107 | }; 108 | -------------------------------------------------------------------------------- /database/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | // Database Adapter 32 | 33 | function DataAdapter(name) { 34 | this.db = {}; 35 | this._put = function() {}; 36 | this._get = function() {}; 37 | 38 | this._use(name); 39 | } 40 | 41 | DataAdapter.prototype._use = function(name) { 42 | if (name === 'leveldb') { 43 | // leveldb: key-value pairs persistent data store 44 | var levelup = require('levelup'); 45 | 46 | // Create our database, supply location and options 47 | this.db = levelup('./mydb'); 48 | this._get = this._get_leveldb 49 | this._put = this._put_nedb; 50 | 51 | return true; 52 | } 53 | 54 | if (name === 'nedb') { 55 | // nedb: In-memory only datastore (no need to load the database) 56 | var Datastore = require('nedb') 57 | 58 | this.db = new Datastore(); 59 | this._get = this._get_nedb; 60 | this._put = this._put_nedb; 61 | 62 | return true; 63 | } 64 | 65 | if (name === 'picodb') { 66 | // nedb: In-memory only datastore (no need to load the database) 67 | var PicoDB = require('picodb'); 68 | 69 | this.db = PicoDB.Create(); 70 | this._get = this._get_picodb; 71 | this._put = this._put_picodb; 72 | 73 | return true; 74 | } 75 | 76 | return true; 77 | } 78 | 79 | DataAdapter.prototype.put = function(hash, tx, cb) { 80 | this._put(hash, tx, cb); 81 | }; 82 | 83 | DataAdapter.prototype.get = function(hash, cb) { 84 | this._get(hash, cb); 85 | }; 86 | 87 | DataAdapter.prototype._put_leveldb = function(hash, tx, cb) { 88 | this.db.put(hash, tx, function (err) { 89 | cb(err); 90 | }); 91 | }; 92 | 93 | DataAdapter.prototype._get_leveldb = function(hash, cb) { 94 | this.db.get(hash, function (err, value) { 95 | cb(err, value); 96 | }); 97 | }; 98 | 99 | DataAdapter.prototype._put_nedb = function(hash, tx, cb) { 100 | var doc = { 101 | hash: hash, 102 | tx: tx 103 | }; 104 | 105 | this.db.insert(doc, function (err) { // Callback is optional 106 | // newDoc is the newly inserted document, including its _id 107 | // newDoc has no key called notToBeSaved since its value was undefined 108 | cb(err); 109 | }); 110 | }; 111 | 112 | DataAdapter.prototype._get_nedb = function(hash, cb) { 113 | this.db.find({ hash: hash }, function (err, docs) { 114 | cb(err, docs); 115 | }); 116 | }; 117 | 118 | DataAdapter.prototype._put_picodb = function(hash, tx, cb) { 119 | var doc = { 120 | hash: hash, 121 | tx: tx 122 | }; 123 | 124 | this.db.insertOne(doc, function (err, results) { 125 | cb(err); 126 | }); 127 | }; 128 | 129 | DataAdapter.prototype._get_picodb = function(hash, cb) { 130 | this.db.find({ hash: hash }).toArray(function (err, docs) { 131 | cb(err, docs); 132 | }); 133 | }; 134 | 135 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 136 | exports = module.exports = DataAdapter; 137 | } 138 | -------------------------------------------------------------------------------- /block/difficulty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.io 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var gaussian = require('gaussian'); 32 | 33 | // difficulties for application processors 34 | var difficulties_ap = [ 35 | '0000000000000000000000000000000000000000000000000000000000000000', 36 | '00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 37 | '0000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 38 | '000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 39 | '00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 40 | '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 41 | '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 42 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 43 | '00F8888888888888888888888888888888888888888888888888888888888888', 44 | '00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 45 | '0F88888888888888888888888888888888888888888888888888888888888888', 46 | '0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 47 | 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 48 | ]; 49 | 50 | // difficulties for micro controllers 51 | var difficulties = [ 52 | '0000000000000000000000000000000000000000000000000000000000000000', 53 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 54 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 55 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 56 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 57 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 58 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 59 | '000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 60 | '00F8888888888888888888888888888888888888888888888888888888888888', 61 | '00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 62 | '0F88888888888888888888888888888888888888888888888888888888888888', 63 | '0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 64 | 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 65 | ]; 66 | 67 | // the mean (μ) of the distribution 68 | var mean = 0; 69 | //the variance (σ^2) of the distribution 70 | var variance = 0.2; 71 | 72 | var distribution = gaussian(mean, variance); 73 | 74 | function Difficulty(x) { 75 | if (!x) x = Math.random(); 76 | 77 | x = x - variance; 78 | 79 | var probability = distribution.pdf(x); 80 | 81 | // prepare for difficulties 82 | var slice = difficulties.length; 83 | var intervals = []; 84 | 85 | for (var i = 0; i < slice; i++) { 86 | intervals[i] = i/slice; 87 | } 88 | intervals[i] = 1; 89 | 90 | // get diffiiculty 91 | var difficulty = -1; 92 | for (i = 0; i < slice ; i++) { 93 | if (probability > intervals[i] 94 | && probability <= intervals[i+1]) { 95 | difficulty = difficulties[i]; 96 | break; 97 | } 98 | } 99 | 100 | this.x = x; 101 | this.probability = probability; 102 | this.difficulty = difficulty; 103 | }; 104 | 105 | Difficulty.prototype.getDifficulty = function() { 106 | return this.difficulty; 107 | }; 108 | 109 | Difficulty.prototype._getProbability = function() { 110 | return this.probability; 111 | }; 112 | 113 | Difficulty.prototype._getX = function() { 114 | return this.x; 115 | }; 116 | 117 | module.exports = Difficulty; 118 | -------------------------------------------------------------------------------- /wot/requestHandlers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var WebSocketClient = require('websocket').client; 32 | 33 | var Handlers = Handlers || {}; 34 | var webSocketConnections = []; 35 | 36 | // Handlers object 37 | Handlers = { 38 | send: function(pathname, connection, clients) { 39 | // the original sender pathname 40 | connection.pathname = pathname; 41 | 42 | /* 43 | * convert sender pathname to viewer pathname 44 | * eg. '/object/:id/send' to '/object/:id/viewer' 45 | */ 46 | var paths = pathname.split('/'); 47 | 48 | // remove the rear string 'send' 49 | var viewer = paths.slice(0, -1).join('/'); 50 | 51 | connection.viewer = viewer + '/viewer'; 52 | connection.statusViewer = viewer + '/status'; 53 | 54 | /* 55 | * initial storage for this viewer 56 | */ 57 | for (var path in clients) { 58 | if (path === connection.viewer) 59 | return; 60 | } 61 | 62 | clients[connection.viewer] = []; 63 | clients[connection.statusViewer] = []; 64 | 65 | // Start forward data to endpoint 66 | var server = connection.server; 67 | var endpoint = server.endpoint; 68 | var wsConn = webSocketConnections[pathname]; 69 | 70 | if (!endpoint) return; 71 | 72 | if (!/^(ws|wss):\/\//.test(endpoint)) { 73 | endpoint = 'ws://' + endpoint; 74 | } 75 | 76 | if (typeof(wsConn) === 'undefined') { 77 | var wsClient = new WebSocketClient(); 78 | 79 | wsClient.on('connectFailed', function(error) { 80 | //console.log('Connect Error: ' + error.toString()); 81 | }); 82 | 83 | wsClient.on('connect', function(conn) { 84 | conn.on('error', function(error) { 85 | }); 86 | conn.on('close', function() { 87 | delete webSocketConnections[pathname]; 88 | }); 89 | conn.on('message', function(message) { 90 | }); 91 | 92 | webSocketConnections[pathname] = conn; 93 | }); 94 | 95 | var uri = endpoint + pathname; 96 | console.info('Connect to endpoint ' + uri); 97 | 98 | // initial websocket connection 99 | return wsClient.connect(uri, ''); 100 | } 101 | 102 | // Send data to endpont 103 | wsConn.sendUTF(data); 104 | }, 105 | 106 | viewer: function(pathname, connection, clients) { 107 | // the original sender pathname 108 | connection.pathname = pathname; 109 | 110 | // Save viewer clients (unlimited clients) 111 | for (var path in clients) { 112 | if (path === pathname) { 113 | clients[path].push(connection); 114 | return; 115 | } 116 | } 117 | 118 | /* 119 | * Not found. There is not a existing sender. 120 | */ 121 | clients[pathname] = []; 122 | clients[pathname].push(connection); 123 | }, 124 | 125 | status: function(pathname, connection, clients) { 126 | // the original sender pathname 127 | connection.pathname = pathname; 128 | 129 | // Save status viewer clients (unlimited clients) 130 | for (var path in clients) { 131 | if (path === pathname) { 132 | clients[path].push(connection); 133 | return; 134 | } 135 | } 136 | 137 | /* 138 | * Not found. There is not a existing status viewer. 139 | */ 140 | clients[pathname] = []; 141 | clients[pathname].push(connection); 142 | }, 143 | 144 | receive: function(pathname, connection, clients) { 145 | } 146 | }; 147 | 148 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 149 | module.exports = Handlers; 150 | -------------------------------------------------------------------------------- /block/mining.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://flowchain.io 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var crypto = require('crypto'); 32 | var merkle = require('merkle'); 33 | var merkleRoot = merkle('sha256'); 34 | var os = require('os'); 35 | 36 | var Block = require('./block'); 37 | 38 | function Miner() { 39 | // Transactions to be mined. 40 | this.txs = []; 41 | 42 | // Previous block. 43 | this.previousBlock = {}; 44 | 45 | // New block. 46 | this.newBlock = new Block(); 47 | 48 | // Secret 49 | this.secret = 'Block0'; 50 | 51 | // is success 52 | this._success = false; 53 | 54 | // Merkle tree 55 | this._tree = []; 56 | 57 | // Jiffy and uptime 58 | this.jiffies = 0; 59 | this.startUptime = os.uptime(); 60 | } 61 | 62 | /** 63 | * Create the merkle root 64 | * 65 | * @param {Object} txs 66 | * @api public 67 | */ 68 | Miner.prototype.setTransactions = function(txs) { 69 | this.txs = txs; 70 | 71 | this._tree = merkleRoot.sync(this.txs); 72 | this.newBlock.merkleRoot = this._tree.level(0)[0]; 73 | }; 74 | 75 | /** 76 | * Create a hash link to the prvious block 77 | * 78 | * @param {Object} block 79 | * @api public 80 | */ 81 | Miner.prototype.setPreviousBlock = function(block) { 82 | this.previousBlock = block; 83 | 84 | this.newBlock.previousHash = this.previousBlock.hash; 85 | this.newBlock.nonce = this.previousBlock.nonce + 1; 86 | this.newBlock.difficulty = this.previousBlock.difficulty; 87 | }; 88 | 89 | Miner.prototype.generateHash = function() { 90 | // The header of the new block. 91 | var header = { 92 | nonce: this.newBlock.nonce, 93 | previousHash: this.newBlock.previousHash, 94 | merkleRoot: this.newBlock.merkleRoot, 95 | timestamp: this.newBlock.timestamp 96 | }; 97 | 98 | var hash = crypto.createHmac('sha256', this.secret) 99 | .update( JSON.stringify(header) ) 100 | .digest('hex'); 101 | 102 | this.newBlock.hash = crypto.createHmac('sha256', hash) 103 | .update('powered by flowchain') 104 | .digest('hex'); 105 | 106 | // Fix mining difficulty 107 | this.jiffies = os.uptime() - this.startUptime; 108 | this._fixDifficultyConsensus(); 109 | 110 | this.newBlock.nonce++; 111 | this._success = ( this.newBlock.hash < this.newBlock.difficulty ); 112 | 113 | return this.newBlock.hash; 114 | }; 115 | 116 | /** 117 | * Return true if the new block found 118 | * 119 | * @return {Boolean} 120 | * @api public 121 | */ 122 | Miner.prototype.isSuccess = function() { 123 | return this._success; 124 | }; 125 | 126 | /* 127 | * Copy states and return the current block 128 | */ 129 | Miner.prototype.getNewBlock = function() { 130 | if (this._success === true) { 131 | this.newBlock.no = this.previousBlock.no + 1; 132 | return new Block(this.newBlock); 133 | } 134 | 135 | return null; 136 | }; 137 | 138 | /* 139 | * Get the block in mining 140 | */ 141 | Miner.prototype.getMiningBlock = function() { 142 | return this.newBlock; 143 | }; 144 | 145 | /* 146 | * Get the nonce of current block 147 | */ 148 | Miner.prototype.getNonce = function() { 149 | return this.newBlock.nonce; 150 | }; 151 | 152 | // TODO: fix mining difficulty by network consensus 153 | Miner.prototype._fixDifficultyConsensus = function() { 154 | var key = this.previousBlock.difficulty; 155 | 156 | this.newBlock.difficulty = key; 157 | }; 158 | 159 | // Fix mining difficulty in the Satoshi method. 160 | Miner.prototype._fixDifficultyNormal = function() { 161 | var key = this.previousBlock.difficulty; 162 | var index = key.length; 163 | 164 | key = '0' + key.slice(0, index - 1); 165 | this.newBlock.difficulty = key; 166 | }; 167 | 168 | // Fix mining difficulty by normal distribution (system uptime) 169 | Miner.prototype._fixDifficulty = function() { 170 | var Difficulty = require('./difficulty'); 171 | 172 | // Seconds 173 | var MAX_UPTIME = 6000; 174 | var x = this.jiffies / MAX_UPTIME; 175 | 176 | var difficulty = new Difficulty(x); 177 | this.newBlock.difficulty = difficulty.getDifficulty(); 178 | }; 179 | 180 | module.exports = Miner; 181 | -------------------------------------------------------------------------------- /tests/suite/test.ChordUtils.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | ChordUtils = require('../../p2p/libs/utils'); 4 | 5 | vows.describe('Chord Utils').addBatch({ 6 | 'Testing key in half ranges': { 7 | 'is in half range': function () { 8 | assert.isTrue( ChordUtils.isInHalfRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 9 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 10 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 11 | }, 12 | 13 | 'key equals n, key ∈ (n, successor]': function () { 14 | assert.isFalse( ChordUtils.isInHalfRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 15 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 16 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 17 | }, 18 | 19 | 'key equals successor, key ∈ (n, successor]': function () { 20 | assert.isTrue( ChordUtils.isInHalfRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad20', 21 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 22 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 23 | }, 24 | 25 | 'out of range': function () { 26 | assert.isFalse( ChordUtils.isInHalfRange('a40990f3092be5541c2edf2d8ce9a7f32a5bfd20', 27 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 28 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 29 | } 30 | }, 31 | 32 | 'Testing key in ranges': { 33 | 'is in range': function () { 34 | assert.isTrue( ChordUtils.isInRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 35 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 36 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 37 | }, 38 | 39 | 'key equals left, key ∈ (left, right)': function () { 40 | assert.isFalse( ChordUtils.isInRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 41 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 42 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 43 | }, 44 | 45 | 'key equals right, key ∈ (left, right)': function () { 46 | assert.isFalse( ChordUtils.isInRange('a40990f3092be5541c2edf2d8ce9a7f32a5bad20', 47 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 48 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 49 | }, 50 | 51 | 'out of range': function () { 52 | assert.isFalse( ChordUtils.isInRange('a40990f3092be5541c2edf2d8ce9a7f32a5bfd20', 53 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad10', 54 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad20') ); 55 | } 56 | }, 57 | 58 | 'Testing getFixFingerId when key is big integer': { 59 | 'exponent is 0': function () { 60 | assert.deepStrictEqual( ChordUtils.getFixFingerId('00000000', 0), 61 | '00000001'); 62 | }, 63 | 64 | 'exponent is 1': function () { 65 | assert.deepStrictEqual( ChordUtils.getFixFingerId('00000000', 1), 66 | '00000002'); 67 | }, 68 | 69 | 'exponent is 2': function () { 70 | assert.deepStrictEqual( ChordUtils.getFixFingerId('00000000', 2), 71 | '00000004'); 72 | }, 73 | 74 | 'exponent is 8': function () { 75 | assert.deepStrictEqual( ChordUtils.getFixFingerId('00000000', 8), 76 | '00000100'); 77 | }, 78 | 79 | 'exponent is 64': function () { 80 | assert.deepStrictEqual( ChordUtils.getFixFingerId('00000000000000000000', 64), 81 | '00010000000000000000'); 82 | }, 83 | }, 84 | 85 | 'Testing getFixFingerId when key is SHA-1 hash': { 86 | 'exponent is 0': function () { 87 | assert.deepStrictEqual( ChordUtils.getFixFingerId('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 0), 88 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad15'); 89 | }, 90 | 91 | 'exponent is 1': function () { 92 | assert.deepStrictEqual( ChordUtils.getFixFingerId('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 1), 93 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad16'); 94 | }, 95 | 96 | 'exponent is 2': function () { 97 | assert.deepStrictEqual( ChordUtils.getFixFingerId('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 2), 98 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bad18'); 99 | }, 100 | 101 | 'exponent is 8': function () { 102 | assert.deepStrictEqual( ChordUtils.getFixFingerId('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 8), 103 | 'a40990f3092be5541c2edf2d8ce9a7f32a5bae14'); 104 | }, 105 | 106 | 'exponent is 64': function () { 107 | assert.deepStrictEqual( ChordUtils.getFixFingerId('a40990f3092be5541c2edf2d8ce9a7f32a5bad14', 64), 108 | 'a40990f3092be5541c2edf2e8ce9a7f32a5bb000'); 109 | }, 110 | } 111 | }).export(module);; 112 | -------------------------------------------------------------------------------- /p2p/libs/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var bigInt = require("big-integer"); 32 | var Log = require('../../utils/Log'); 33 | var TAG = 'P2P'; 34 | 35 | var hexToBigInt = function(hex) { 36 | var v = bigInt(hex.replace('0x', ''), 16); 37 | return v; 38 | }; 39 | 40 | var Utils = { 41 | DebugVerbose: false, 42 | 43 | DebugNodeJoin: false, 44 | DebugFixFingers: false, 45 | DebugServer: false, 46 | DebugSuccessor: false, 47 | DebugPredecessor: false, 48 | DebugStabilize: false, 49 | DebugMessage: false, 50 | DebugFailureCheck: true, 51 | 52 | DebugPrintFingerTable: true, 53 | 54 | /** 55 | * Generate a hash key by SHA1. The key is used as identifier (ID) of each node. 56 | * 57 | * @param {String} text 58 | * @return {String} 59 | */ 60 | hash: function(text) { 61 | var data = ('CHORD..++' + text + new Date() + Math.floor(Math.random()*999999)); 62 | var Crypto = require('crypto'); 63 | var key = Crypto.createHash('sha1').update(data).digest('hex'); 64 | 65 | return key; 66 | }, 67 | 68 | /** 69 | * Generate a test ID. The key is in [1, 9999] with the length of 4 bytes. 70 | * 71 | * @param {String} text 72 | * @return {String} 73 | */ 74 | hashTestId: function(n) { 75 | if (n) 76 | return n; 77 | 78 | var data = (Math.floor(Math.random()*9999) + 1).toString(); 79 | var length = data.split('').length - 4; 80 | 81 | // Left pads 82 | while (length++ < 0) 83 | data = '0' + data; 84 | 85 | return data; 86 | }, 87 | 88 | /** 89 | * Testing if key ∈ (n, successor] 90 | * 91 | * @param {String} key 92 | * @param {String} n 93 | * @param {String} successor 94 | * @return {Boolean} 95 | * @api private 96 | */ 97 | isInHalfRange: function(key, n, successor) { 98 | if (Utils.DebugSuccessor) 99 | console.info(key + ' isInHalfRange [ ' + n + ', ' + successor + ']') 100 | 101 | var bKey = hexToBigInt(key); 102 | var bN = hexToBigInt(n); 103 | var bSuccessor = hexToBigInt(successor); 104 | 105 | // n < successor 106 | if (bN.compare(bSuccessor) <= -1) { 107 | // (key > n && key <= successor) || (n === successor && key < n) 108 | return (bKey.compare(bN) >= 1 && bKey.compare(bSuccessor) <= 0) || (bN.compare(bSuccessor) === 0 && bKey.compare(bN) <= -1); 109 | } else { 110 | // (key > successor && key <= n) || (n === successor && key < n) 111 | return (bKey.compare(bSuccessor) >= 1 && bKey.compare(bN) <= 0) || (bN.compare(bSuccessor) === 0 && bKey.compare(bN) <= -1); 112 | } 113 | }, 114 | 115 | /** 116 | * Testing if key ∈ (left, right) 117 | * 118 | * @param {String} key 119 | * @param {String} left 120 | * @param {String} right 121 | * @return {Boolean} 122 | * @api private 123 | */ 124 | isInRange: function(key, left, right) { 125 | if (Utils.DebugFixFingers || Utils.DebugStabilize) 126 | console.info(key + ' isInRange [ ' + left + ', ' + right + ']') 127 | 128 | var bKey = hexToBigInt(key); 129 | var bLeft = hexToBigInt(left); 130 | var bRight = hexToBigInt(right); 131 | 132 | // left < right 133 | if (bLeft.compare(bRight) <= -1) { 134 | // (key > left && key < right) || (left === right && key !== left) 135 | return (bKey.compare(bLeft) >= 1 && bKey.compare(bRight) <= -1) || (bLeft.compare(bRight) === 0 && bKey.compare(bLeft) !== 0); 136 | } else { 137 | // (key > right && key < left) || (left === right && key !== left) 138 | return (bKey.compare(bRight) >= 1 && bKey.compare(bLeft) <= -1) || (bLeft.compare(bRight) === 0 && bKey.compare(bLeft) !== 0); 139 | } 140 | }, 141 | 142 | getNextFingerId: function(n, i, m) { 143 | var result = n + Math.pow(2, i - 1); 144 | var result = result % Math.pow(2, m); 145 | 146 | return result; 147 | }, 148 | 149 | /** 150 | * The new key equals to key + 2 ^ exponent. 151 | * 152 | * @param {String} key 153 | * @param {Integer} exponent 154 | */ 155 | getFixFingerId: function(key, exponent) { 156 | var id = []; 157 | var result = key.split(''); 158 | var index = result.length - 1; 159 | var carry = Math.pow(2, exponent); 160 | 161 | while (index >= 0) { 162 | var d = parseInt(result[index], 16) + carry; 163 | carry = 0; 164 | if (d > 0xf) { 165 | carry = d - (d % 16); 166 | carry = carry / 16; 167 | 168 | d = d % 16; 169 | } 170 | result[index] = d.toString(16); 171 | --index; 172 | } 173 | return result.join(''); 174 | } 175 | }; 176 | 177 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 178 | module.exports = Utils; 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flowchain-ledger 2 | > Flowchain distributed ledgers in JavaScript 3 | 4 | ![](https://flowchain.co/static/logo-text@128.png) 5 | 6 | [![Build Status](https://travis-ci.org/flowchain/flowchain-ledger.svg?branch=master)](https://travis-ci.org/flowchain/flowchain-ledger) 7 | 8 | Flowchain-ledger is a distributed ledger technology (DLT) for the Internet of Things (IoT) devices (aka. IoT Blockchain). The Flowchain software framework is designed from the ground up to fit current IoT needs. 9 | 10 | This is the **official** Flowchain distributed ledger library, which provides the following APIs: 11 | * Create data transactions 12 | * Submit data transactions 13 | 14 | # About Flowchain 15 | 16 | Flowchain is a software framework for the IoT blockchain to provide a distributed ledger programming framework for peer-to-peer IoT networks and real-time data transactions, and ```flowchain-ledger``` is the Node.js distribution for heterogeneous hardware devices, such as MediaTek LinkIt Smart 7688, Raspberry Pi, laptops, smart phones and etc. 17 | 18 | # Abstract 19 | 20 | Flowchain aims to implement a blockchain that can provide near real-time transactions for the IoT. The Flowchain technology can ensure chunked data (data streams) transactions by using the *virtual blocks* concept which is proposed by Flowchain acamedic papers. The virtual blocks can be combined with emerging public blockchains, such as Ethereum and Hyperledger which is optimized specifically for the use case of off-chain transactions. If you need such "Hybrid Blockchain" network for the IoT, please refer to [flowchain-hybrid](https://github.com/flowchain/flowchain-hybrid). 21 | 22 | 23 | The FlowchainCoin (symbol: FLC) is the Ethereum ERC20 token for tokenized assets that use in the Flowchain network. Tokens offer can support the transfer of assets from one trusted party to another. 24 | 25 | # Features 26 | 27 | * Mining-based Proof-of-Stake (PoS) 28 | * Run on every IoT node 29 | * RPC over the Websocket protocol 30 | * Distributed Hash Table (DHT) by the Chord protocol 31 | * The device server in Web of Things (WoT) architecture 32 | 33 | # Prerequisite 34 | 35 | * Node.js v8+ 36 | * [See Flowchain Documentation](https://github.com/flowchain/flowchain-ledger/wiki) 37 | 38 | 39 | # Getting started 40 | 41 | Install the Flowchain distributed ledger via npm. 42 | 43 | ``` 44 | npm install flowchain.js 45 | ``` 46 | 47 | Create the `boot.js` file and use the `flowchain.js` library to submit a data transaction. 48 | 49 | ``` 50 | var PeerNode = require('flowchain.js').PeerNode; 51 | var node = new PeerNode();; 52 | node.submit(data); 53 | ``` 54 | 55 | Please use [flowchain-hybrid](https://github.com/flowchain/flowchain-hybrid) which is the blockchain application built on flowchain-ledger. 56 | 57 | 58 | ## Install via Github 59 | 60 | You can also download this repo. 61 | 62 | ``` 63 | $ git clone https://github.com/flowchain/flowchain-ledger.git 64 | $ cd flowchain-ledger 65 | $ npm install 66 | ``` 67 | 68 | # Configuration 69 | 70 | ``` 71 | $ export HOST=192.168.1.1 72 | $ export PORT=8000 73 | $ node boot.js 74 | ``` 75 | 76 | The boot node is now running at the address ```192.168.1.1:8000```. Please modify the IP address and listening port number to match your own environment. Subsequently, to start a peer node and join the boot node: 77 | 78 | ``` 79 | $ export HOST=192.168.1.2 ; Peer Node (My IP address) 80 | $ export PORT=8001 ; Peer Node (My listening port) 81 | $ export PEER_ADDR=192.168.1.1 ; Boot Node (The node intend to join) 82 | $ export PEER_PORT=8000 ; Boot Node (The node intend to join) 83 | $ node peer.js ; Start the new peer node 84 | ``` 85 | 86 | The new peer node will run at the address ```192.168.1.2:8001```, and subsequently join the boot node at the address ```192.168.1.1:8000```. The peer node will intend to join the p2p network. Furthuremore, you can start another peer node and join the p2p network through the boot node or one of the peer nodes. For example, to start another peer node and join the peer node at ```192.168.1.2:8001```: 87 | 88 | ``` 89 | $ export HOST=192.168.1.2 90 | $ export HOST=8002 91 | $ export PEER_ADDR=192.168.1.2 92 | $ export PEER_PORT=8001 93 | $ node peer.js 94 | ``` 95 | 96 | # Publication 97 | 98 | ## Presentation 99 | 100 | * [Flowchain: A Case Study on Building a Blockchain for the IoT in Node.js - Jollen Chen, Flowchain.co 101 | ](https://lc3china2017.sched.com/event/Aedw/flowchain-a-case-study-on-building-a-blockchain-for-the-iot-in-nodejs-ce-jollen-chen-flowchainco), LinuxCon + ContainerCon + CloudOpen (LC3), Beijing, China, June 19-20, 2017. 102 | 103 | ## Bibliography 104 | 105 | This work is based on the research papers. 106 | 107 | [1] Chen, J. (2017). [Flowchain: A Distributed Ledger Designed for Peer-to-Peer IoT Networks and Real-time Data Transactions.](https://sites.google.com/site/lddleswc17/program) In: 2nd International Workshop on Linked Data and Distributed Ledgers. Portoroz. 108 | 109 | [2] Chen, J. (2017). [Devify: Decentralized Internet of Things Software Framework for a Peer-to-Peer and Interoperable IoT Device](https://sites.google.com/view/aiotas2017/program?authuser=0) In: Advances in IoT Architecture and Systems. Toronto, Canada. 110 | 111 | ## How to Cite 112 | 113 | ``` 114 | @article{flowchain_2017, 115 | title={Flowchain: A Distributed Ledger Designed for Peer-to-Peer IoT Networks and Real-time Data Transactions}, 116 | journal={Proceedings of the 2nd International Workshop on Linked Data and Distributed Ledgers (LDDL2)}, 117 | author={Chen, Jollen}, 118 | year={2017} 119 | }, 120 | 121 | @article{devify_2017, 122 | title={Devify: Decentralized Internet of Things Software Framework for a Peer-to-Peer and Interoperable IoT Device}, 123 | journal={Proceedings of the Workshop on Advances in IoT Architecture and Systems (AIoTAS2017)}, 124 | author={Chen, Jollen}, 125 | year={2017} 126 | }, 127 | ``` 128 | 129 | # License 130 | 131 | Copyright (C) 2016-present Jollen. The source code is licensed under the MIT license found in the [LICENSE](LICENSE) file. 132 | -------------------------------------------------------------------------------- /wot/broker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | /* 32 | * Module dependencies. 33 | */ 34 | var http = require("http") 35 | , url = require("url") 36 | , cluster = require('cluster') 37 | , WebSocketServer = require('websocket').server 38 | , EventEmitter = require('events').EventEmitter 39 | , util = require('util'); 40 | 41 | /** 42 | * Expose `WebsocketBroker` constructor. 43 | */ 44 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 45 | exports = module.exports = WebsocketBroker; 46 | } 47 | 48 | /** 49 | * Initialize a new `WebsocketBroker` with the given `options`. 50 | * 51 | * @param {Object} options 52 | * @api private 53 | */ 54 | function WebsocketBroker(options) { 55 | // Superclass Constructor 56 | EventEmitter.call(this); 57 | 58 | options = options || {}; 59 | this.clientsPath = []; 60 | this.host = options.host || 'localhost'; 61 | this.port = options.port || 8000; 62 | this.endpoint = options.endpoint || null; 63 | this.thingid = options.thingid || '5550937980d51931b3000009'; 64 | this.wsServer = null; 65 | this.httpServer = null; 66 | } 67 | 68 | util.inherits(WebsocketBroker, EventEmitter); 69 | 70 | /** 71 | * Initialize a new `WebsocketBroker` with the given `options`. 72 | * 73 | * @param {Object} request 74 | * @param {Object} response 75 | * @api private 76 | */ 77 | WebsocketBroker.prototype.onRequest = function(request, response) { 78 | response.writeHead(200, {"Content-Type": "text/plain"}); 79 | response.end(); 80 | }; 81 | 82 | /** 83 | * Initialize a new `WebsocketBroker` with the given `options`. 84 | * 85 | * @param {String} path 86 | * @param {Object} data 87 | * @api private 88 | */ 89 | 90 | WebsocketBroker.prototype.dispatchData = function(path, data) { 91 | var connections = this.clientsPath[path]; 92 | 93 | if (typeof(connections) === 'undefined') 94 | return; 95 | 96 | for (var i = 0; i < connections.length; i++) { 97 | connections[i].sendUTF(data); 98 | } 99 | }; 100 | 101 | /** 102 | * Initialize a new `WebsocketBroker` with the given `options`. 103 | * 104 | * @param {Object} request 105 | * @param {Object} response 106 | * @api private 107 | */ 108 | 109 | WebsocketBroker.prototype.dispatchStatus = function(path, data) { 110 | var connections = this.clientsPath[path]; 111 | 112 | if (typeof connections === 'undefined') 113 | return; 114 | 115 | for (var i = 0; i < connections.length; i++) { 116 | connections[i].sendUTF(data); 117 | } 118 | }; 119 | 120 | /** 121 | * Start the Websocket server. 122 | * 123 | * @param {Object} route 124 | * @return {} 125 | * @api public 126 | */ 127 | WebsocketBroker.prototype.start = function(route, handlers) { 128 | var self = this; 129 | 130 | // Use options or environment variables 131 | var port = self.port || process.env['PORT']; 132 | var host = self.host || process.env['HOST']; 133 | var endpoint = self.endpoint || process.env['ENDPOINT']; 134 | 135 | // Update attributes 136 | this.port = port; 137 | this.host = host; 138 | this.endpoint = endpoint; 139 | 140 | var httpServer = http.createServer(this.onRequest).listen(port, host, function() { 141 | console.info('node is running at ws://' + self.host + ':' + self.port); 142 | }); 143 | 144 | var wsServer = new WebSocketServer({ 145 | httpServer: httpServer, 146 | autoAcceptConnections: false 147 | }); 148 | 149 | this.httpServer = httpServer; 150 | this.wsServer = wsServer; 151 | 152 | /** 153 | * handlers 154 | */ 155 | var onWsRequest = function(request) { 156 | var connection = request.accept('', request.origin); 157 | 158 | // bind information 159 | connection.server = self; 160 | 161 | route(request.resource, connection, handlers, self.clientsPath); 162 | 163 | // register this thing 164 | self.emit('newThing', { 165 | name: connection.pathname 166 | }); 167 | 168 | connection.on('message', onWsConnMessage); 169 | connection.on('close', onWsConnClose); 170 | 171 | if (typeof (connection.statusViewer) !== 'undefined') 172 | self.dispatchStatus(connection.statusViewer, JSON.stringify({ isAlive: true })); 173 | }; 174 | 175 | var onWsConnMessage = function(message) { 176 | // Dispatching request message 177 | self.emit('data', { 178 | data: message.utf8Data, 179 | pathname: this.pathname 180 | }); 181 | 182 | // Is it a sender ? Yes, then push data to all viewers. 183 | if (typeof (this.viewer) !== 'undefined') 184 | self.dispatchData(this.viewer, message.utf8Data); 185 | 186 | if (typeof (this.statusViewer) !== 'undefined') 187 | self.dispatchStatus(this.statusViewer, JSON.stringify({ isAlive: true })); 188 | }; 189 | 190 | var onWsConnect = function(webSocketConnection) { 191 | }; 192 | 193 | var onWsConnClose = function(reasonCode, description) { 194 | if (typeof (this.statusViewer) !== 'undefined') 195 | self.dispatchStatus(this.statusViewer, JSON.stringify({ isAlive: false })); 196 | }; 197 | 198 | wsServer.on('request', onWsRequest); 199 | wsServer.on('connect', onWsConnect); 200 | }; 201 | 202 | /** 203 | * Shutdown the Websocket server. 204 | * 205 | * @param cb {Function} The complete callback 206 | * @return {} 207 | * @api public 208 | */ 209 | WebsocketBroker.prototype.shutdown = function(cb) { 210 | var self = this; 211 | 212 | this.httpServer.close(function() { 213 | self.wsServer.shutDown(); 214 | if (typeof cb === 'function') return cb(); 215 | }); 216 | }; 217 | -------------------------------------------------------------------------------- /nodes/boot.js: -------------------------------------------------------------------------------- 1 | var Log = require('../utils/Log'); 2 | var TAG = 'Flowchain/Ledger'; 3 | var TAG_DB = 'Picodb'; 4 | var TAG_QUERY = 'Flowchain/Ledger/Query'; 5 | 6 | // Chord protocols 7 | var Chord = require('../p2p/libs/message'); 8 | 9 | // Import the Flowchain library 10 | var Flowchain = require('../libs'); 11 | 12 | // Import Websocket server 13 | var server = Flowchain.WoTServer; 14 | 15 | // Utils 16 | var crypto = Flowchain.Crypto; 17 | 18 | // Database 19 | var Database = Flowchain.DatabaseAdapter; 20 | var db = new Database('picodb'); 21 | 22 | /** 23 | * The `onmessage` callback. Handling Chord messages 24 | * which is assigned/forwarded to this node. 25 | * 26 | * @param {Object} req 27 | * @param {Object} res 28 | * @return {Boolean} 29 | */ 30 | var onmessage = function(req, res) { 31 | var payload = req.payload; 32 | var block = req.block; 33 | var node = req.node; 34 | 35 | var data = JSON.parse(payload.data); 36 | var message = data.message; 37 | var from = data.from; 38 | 39 | // Key of the data 40 | var key = message.id; 41 | // Data 42 | var tx = message.data; 43 | 44 | // Get a block 45 | if (!block) { 46 | Log.i(TAG, 'No virtual blocks now, ignore #' + key); 47 | return; 48 | } 49 | 50 | // Block hash as the secret and data key as the context 51 | var hash = crypto.createHmac('sha256', block.hash) 52 | .update( key ) 53 | .digest('hex'); 54 | 55 | // Generate an asset 56 | var asset = { 57 | // Data key 58 | key: key 59 | }; 60 | 61 | // Give out the asset 62 | //res.send(asset); 63 | 64 | // Establish a linked data description 65 | var device = { 66 | '@context': [ 67 | 'http://w3c.github.io/wot/w3c-wot-td-contxt.jsonld' 68 | ], 69 | 'name': '', 70 | }; 71 | 72 | // Send ACK back 73 | /* 74 | var ack = { 75 | key: key, 76 | status: 'ACK' 77 | }; 78 | 79 | if (node.address !== from.address || 80 | node.port !== from.port) { 81 | node.send(from, ack); 82 | } 83 | */ 84 | 85 | db.put(hash, tx, function (err) { 86 | if (err) { 87 | return Log.e(TAG, 'Ooops! onmessage =', err) // some kind of I/O error 88 | } 89 | 90 | Log.v(TAG, 'Transactions #' + key + ' found in Block#' + block.no); 91 | 92 | // Submit data transactions to the p2p network. 93 | // The data is sent via our virtual blocks (the local blockchains) to 94 | // hybrid node for consensus and verfication. 95 | // The boot node is a hybrid node. 96 | node.submitVirtualBlocks([{ 97 | height: block.no, 98 | merkleRoot: hash, 99 | miner: { 100 | id: node.id, 101 | // add lambda and puzzle solutions 102 | } 103 | }]); 104 | 105 | Log.v(TAG, 'Submit virtual blocks #' + hash); 106 | 107 | // Get the IPFS hash (filename) 108 | /*var ipfsVideoHash = ipfs.add(tx 109 | , function(err, res) { 110 | if (err) { 111 | Log.i(TAG_IPFS, 'Error: ' + err); 112 | return; 113 | } 114 | var hash = res[0].hash; 115 | var size = res[0].size; 116 | 117 | Log.i(TAG_IPFS, 'IPFS hash: ' + hash + '. Size: ' + size); 118 | Log.v(TAG, 'Transactions #' + key + ' found in Block#' + block.no); 119 | } 120 | );*/ 121 | 122 | // fetch by key 123 | db.get(hash, function (err, value) { 124 | if (err) { 125 | return Log.e(TAG, 'Ooops! onmessage =', err) // likely the key was not found 126 | } 127 | 128 | Log.v(TAG, 'Verifying tx =', key); 129 | 130 | res.read(key); 131 | }); 132 | }); 133 | }; 134 | 135 | /** 136 | * The `onstart` callback triggered when the server was successfully 137 | * started. 138 | * 139 | * @param {Object} req 140 | * @param {Object} res 141 | * @return {Boolean} 142 | */ 143 | var onstart = function(req, res) { 144 | // Chord node ID 145 | var id = req.node.id; 146 | var address = req.node.address; 147 | }; 148 | 149 | /** 150 | * The `onquery` callback. Handling ledger queries data in the local blocks. 151 | * It should check whether the transaction was stored in the local ledgers. 152 | * 153 | * req.block: 154 | * 155 | * { origin: 156 | * { 157 | * address: '192.168.0.105', 158 | * port: 8000, 159 | * id: '4b0618b5030220ef616c0b9ee92b2936d695c4e0' 160 | * }, 161 | * key: 'ce6b87119dc5e92040172eb6045828acb569bacc' 162 | * } 163 | * 164 | * @param {Object} req 165 | * @param {Object} res 166 | * @return {Boolean} 167 | */ 168 | var onquery = function(req, res) { 169 | var tx = req.tx; 170 | var block = req.block; 171 | 172 | if (!block) return; 173 | 174 | // Block hash as the secret and data key as the context 175 | var hash = crypto.createHmac('sha256', block.hash) 176 | .update( tx.key ) 177 | .digest('hex'); 178 | 179 | db.get(hash, function (err, value) { 180 | if (err) { 181 | return Log.e(TAG, 'Ooops! onmessage =', err) // likely the key was not found 182 | } 183 | 184 | if (!value || typeof(value) === 'undefined') { 185 | return ; 186 | } 187 | 188 | if (value.length < 1) { 189 | return; 190 | } 191 | 192 | /* 193 | * The raw data in the local database: 194 | * 195 | * { temperature: 23, 196 | * source: { 197 | * address: '192.168.0.105', port: 8000 198 | * } 199 | * } 200 | */ 201 | var payload = value[0].tx; 202 | 203 | payload.source = { 204 | address: req.node.address, 205 | port: req.node.port 206 | }; 207 | 208 | Log.v(TAG_QUERY, 'Verified #' + tx.key); 209 | }); 210 | }; 211 | 212 | /** 213 | * The `ondata` callback. 214 | * 215 | * @param {Object} req 216 | * @param {Object} res 217 | * @return {Boolean} 218 | */ 219 | var ondata = function(req, res) { 220 | var data = req.data; 221 | var put = res.save; 222 | 223 | put(data); 224 | }; 225 | 226 | /** 227 | * The `onedge` callback. 228 | * 229 | * @param {Object} req 230 | * @param {Object} res 231 | * @return {Boolean} 232 | */ 233 | var onedge = function(req, res) { 234 | return; 235 | }; 236 | 237 | function PeerNode() { 238 | this.server = server; 239 | } 240 | 241 | /** 242 | * Submit a transaction to the Flowchain p2p network 243 | * 244 | * @param {Object} data 245 | * @return {Object} 246 | * @api public 247 | */ 248 | PeerNode.prototype.submit = function(data) { 249 | this.server.save(data); 250 | } 251 | 252 | /** 253 | * Create a Flowchain Ledger node 254 | * 255 | * @param {Object} options 256 | * @return {Object} 257 | * @api public 258 | */ 259 | PeerNode.prototype.start = function(options) { 260 | var peerAddr = 'localhost'; 261 | var peerPort = '8000'; 262 | if (!options) options = {}; 263 | 264 | if (options.join) { 265 | peerAddr = options.join.address || peerAddr; 266 | peerPort = options.join.port || peerPort; 267 | } 268 | 269 | this.server.start({ 270 | onstart: options.onstart || onstart, 271 | onmessage: options.onmessage || onmessage, 272 | onquery: options.onquery || onquery, 273 | ondata: options.ondata || ondata, 274 | onedge: options.onedge || onedge, 275 | join: { 276 | address: process.env['PEER_ADDR'] || peerAddr, 277 | port: process.env['PEER_PORT'] || peerPort 278 | } 279 | }); 280 | }; 281 | 282 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 283 | module.exports = PeerNode; 284 | } 285 | 286 | // Start the server 287 | if (!module.parent) { 288 | server.start({ 289 | onstart: onstart, 290 | onmessage: onmessage, 291 | onquery: onquery, 292 | ondata: ondata 293 | }); 294 | } -------------------------------------------------------------------------------- /nodes/peer.js: -------------------------------------------------------------------------------- 1 | var Log = require('../utils/Log'); 2 | var TAG = 'Flowchain/Ledger'; 3 | var TAG_DB = 'Picodb'; 4 | var TAG_QUERY = 'Flowchain/Ledger/Query'; 5 | 6 | // Chord protocols 7 | var Chord = require('../p2p/libs/message'); 8 | 9 | // Import the Flowchain library 10 | var Flowchain = require('../libs'); 11 | 12 | // Import Websocket server 13 | var server = Flowchain.WoTServer; 14 | 15 | // Utils 16 | var crypto = Flowchain.Crypto; 17 | 18 | // Database 19 | var Database = Flowchain.DatabaseAdapter; 20 | var db = new Database('picodb'); 21 | 22 | /** 23 | * The `onmessage` callback. Handling Chord messages 24 | * which is assigned/forwarded to this node. 25 | * 26 | * @param {Object} req 27 | * @param {Object} res 28 | * @return {Boolean} 29 | */ 30 | var onmessage = function(req, res) { 31 | var payload = req.payload; 32 | var block = req.block; 33 | var node = req.node; 34 | 35 | var data = JSON.parse(payload.data); 36 | var message = data.message; 37 | var from = data.from; 38 | 39 | // Key of the data 40 | var key = message.id; 41 | // Data 42 | var tx = message.data; 43 | 44 | // Get a block 45 | if (!block) { 46 | Log.i(TAG, 'No virtual blocks now, ignore #' + key); 47 | return; 48 | } 49 | 50 | // Block hash as the secret and data key as the context 51 | var hash = crypto.createHmac('sha256', block.hash) 52 | .update( key ) 53 | .digest('hex'); 54 | 55 | // Generate an asset 56 | var asset = { 57 | // Data key 58 | key: key 59 | }; 60 | 61 | // Give out the asset 62 | //res.send(asset); 63 | 64 | // Establish a linked data description 65 | var device = { 66 | '@context': [ 67 | 'http://w3c.github.io/wot/w3c-wot-td-contxt.jsonld' 68 | ], 69 | 'name': '', 70 | }; 71 | 72 | // Send ACK back 73 | /* 74 | var ack = { 75 | key: key, 76 | status: 'ACK' 77 | }; 78 | 79 | if (node.address !== from.address || 80 | node.port !== from.port) { 81 | node.send(from, ack); 82 | } 83 | */ 84 | 85 | db.put(hash, tx, function (err) { 86 | if (err) { 87 | return Log.e(TAG, 'Ooops! onmessage =', err) // some kind of I/O error 88 | } 89 | 90 | Log.v(TAG, 'Transactions #' + key + ' found in Block#' + block.no); 91 | 92 | // Submit data transactions to the p2p network. 93 | // The data is sent via our virtual blocks (the local blockchains) to 94 | // hybrid node for consensus and verfication. 95 | // The boot node is a hybrid node. 96 | node.submitVirtualBlocks([{ 97 | height: block.no, 98 | merkleRoot: hash, 99 | miner: { 100 | id: node.id, 101 | // add lambda and puzzle solutions 102 | } 103 | }]); 104 | 105 | Log.v(TAG, 'Submit virtual blocks #' + hash); 106 | 107 | // Get the IPFS hash (filename) 108 | /*var ipfsVideoHash = ipfs.add(tx 109 | , function(err, res) { 110 | if (err) { 111 | Log.i(TAG_IPFS, 'Error: ' + err); 112 | return; 113 | } 114 | var hash = res[0].hash; 115 | var size = res[0].size; 116 | 117 | Log.i(TAG_IPFS, 'IPFS hash: ' + hash + '. Size: ' + size); 118 | Log.v(TAG, 'Transactions #' + key + ' found in Block#' + block.no); 119 | } 120 | );*/ 121 | 122 | // fetch by key 123 | db.get(hash, function (err, value) { 124 | if (err) { 125 | return Log.e(TAG, 'Ooops! onmessage =', err) // likely the key was not found 126 | } 127 | 128 | Log.v(TAG, 'Verifying tx =', key); 129 | 130 | res.read(key); 131 | }); 132 | }); 133 | }; 134 | 135 | /** 136 | * The `onstart` callback triggered when the server was successfully 137 | * started. 138 | * 139 | * @param {Object} req 140 | * @param {Object} res 141 | * @return {Boolean} 142 | */ 143 | var onstart = function(req, res) { 144 | // Chord node ID 145 | var id = req.node.id; 146 | var address = req.node.address; 147 | }; 148 | 149 | /** 150 | * The `onquery` callback. Handling ledger queries data in the local blocks. 151 | * It should check whether the transaction was stored in the local ledgers. 152 | * 153 | * req.block: 154 | * 155 | * { origin: 156 | * { 157 | * address: '192.168.0.105', 158 | * port: 8000, 159 | * id: '4b0618b5030220ef616c0b9ee92b2936d695c4e0' 160 | * }, 161 | * key: 'ce6b87119dc5e92040172eb6045828acb569bacc' 162 | * } 163 | * 164 | * @param {Object} req 165 | * @param {Object} res 166 | * @return {Boolean} 167 | */ 168 | var onquery = function(req, res) { 169 | var tx = req.tx; 170 | var block = req.block; 171 | 172 | if (!block) return; 173 | 174 | // Block hash as the secret and data key as the context 175 | var hash = crypto.createHmac('sha256', block.hash) 176 | .update( tx.key ) 177 | .digest('hex'); 178 | 179 | db.get(hash, function (err, value) { 180 | if (err) { 181 | return Log.e(TAG, 'Ooops! onmessage =', err) // likely the key was not found 182 | } 183 | 184 | if (!value || typeof(value) === 'undefined') { 185 | return ; 186 | } 187 | 188 | if (value.length < 1) { 189 | return; 190 | } 191 | 192 | /* 193 | * The raw data in the local database: 194 | * 195 | * { temperature: 23, 196 | * source: { 197 | * address: '192.168.0.105', port: 8000 198 | * } 199 | * } 200 | */ 201 | var payload = value[0].tx; 202 | 203 | payload.source = { 204 | address: req.node.address, 205 | port: req.node.port 206 | }; 207 | 208 | Log.v(TAG_QUERY, 'Verified #' + tx.key); 209 | }); 210 | }; 211 | 212 | /** 213 | * The `ondata` callback. 214 | * 215 | * @param {Object} req 216 | * @param {Object} res 217 | * @return {Boolean} 218 | */ 219 | var ondata = function(req, res) { 220 | var data = req.data; 221 | var put = res.save; 222 | 223 | put(data); 224 | }; 225 | 226 | /** 227 | * The `onedge` callback. 228 | * 229 | * @param {Object} req 230 | * @param {Object} res 231 | * @return {Boolean} 232 | */ 233 | var onedge = function(req, res) { 234 | return; 235 | }; 236 | 237 | function PeerNode() { 238 | this.server = server; 239 | } 240 | 241 | /** 242 | * Submit a transaction to the Flowchain p2p network 243 | * 244 | * @param {Object} data 245 | * @return {Object} 246 | * @api public 247 | */ 248 | PeerNode.prototype.submit = function(data) { 249 | this.server.save(data); 250 | } 251 | 252 | /** 253 | * Create a Flowchain Ledger node 254 | * 255 | * @param {Object} options 256 | * @return {Object} 257 | * @api public 258 | */ 259 | PeerNode.prototype.start = function(options) { 260 | var peerAddr = 'localhost'; 261 | var peerPort = '8000'; 262 | if (!options) options = {}; 263 | 264 | if (options.join) { 265 | peerAddr = options.join.address || peerAddr; 266 | peerPort = options.join.port || peerPort; 267 | } 268 | 269 | this.server.start({ 270 | onstart: options.onstart || onstart, 271 | onmessage: options.onmessage || onmessage, 272 | onquery: options.onquery || onquery, 273 | ondata: options.ondata || ondata, 274 | onedge: options.onedge || onedge, 275 | join: { 276 | address: process.env['PEER_ADDR'] || peerAddr, 277 | port: process.env['PEER_PORT'] || peerPort 278 | } 279 | }); 280 | }; 281 | 282 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") { 283 | module.exports = PeerNode; 284 | } 285 | 286 | // Start the server 287 | if (!module.parent) { 288 | server.start({ 289 | onstart: onstart, 290 | onmessage: onmessage, 291 | onquery: onquery, 292 | ondata: ondata, 293 | join: { 294 | address: process.env['PEER_ADDR'] || 'localhost', 295 | port: process.env['PEER_PORT'] || '8000' 296 | } 297 | }); 298 | } -------------------------------------------------------------------------------- /wot/framework.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * WoT.City Open Source Project 4 | * 5 | * Copyright 2015 Jollen 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | "use strict"; 28 | 29 | /** 30 | * Expose `Automation` class. (NodeJS) 31 | */ 32 | if (typeof(module) !== "undefined" && typeof(exports) !== "undefined") { 33 | exports = module.exports = Automation; 34 | } 35 | 36 | /** 37 | * Util Modules 38 | */ 39 | var Backbone = require('backbone'); 40 | var _ = require('underscore'); 41 | var merge = require('utils-merge'); 42 | 43 | /* 44 | * Class 45 | */ 46 | function Automation(options) { 47 | this.super(); 48 | 49 | // initialize the private options 50 | this._options = {}; 51 | 52 | // the server implementation 53 | if (typeof(options.server) === 'object') { 54 | this._options.server = options.server; 55 | } 56 | 57 | // Merge server instance with the framework instance 58 | // (copy our methods to server instance). 59 | return merge(this, this._options.server); 60 | }; 61 | 62 | /** 63 | * EventAggregator can be used to decouple various parts 64 | * of an application through event-driven architecture. 65 | * 66 | * Borrowing this code from https://github.com/marionettejs/backbone.wreqr/blob/master/src/wreqr.eventaggregator.js 67 | */ 68 | Automation.EventAggregator = function () { 69 | 70 | var EA = function(){}; 71 | 72 | // Copy the *extend* function used by Backbone's classes 73 | EA.extend = Backbone.Model.extend; 74 | 75 | // Copy the basic Backbone.Events on to the event aggregator 76 | _.extend(EA.prototype, Backbone.Events); 77 | 78 | return new EA(); 79 | }; 80 | 81 | /** 82 | * Container 83 | * 84 | * The container to store, retrieve child elements. 85 | * Borrowing this code from https://github.com/marionettejs/backbone.babysitter 86 | */ 87 | Automation.ChildElementContainer = function (context) { 88 | 89 | // Container Constructor 90 | // --------------------- 91 | 92 | // the container of new things with its given unique name, model and implementation 93 | // see: https://github.com/wotcity/web-of-things-framework 94 | var Container = function() { 95 | this._names = []; 96 | this._models = []; 97 | this._implementations = []; 98 | }; 99 | 100 | // Container Methods 101 | // ----------------- 102 | _.extend(Container.prototype, { 103 | // Add an element to this container. Stores the element 104 | // by `cid` and makes it searchable by the model 105 | // cid (and model itself). 106 | add: function(options){ 107 | var name = options.name 108 | , model = options.model 109 | , implementation = options.implementation 110 | , cid = options.cid; 111 | 112 | // save the new things by internal cid 113 | this._names[cid] = name; 114 | this._models[cid] = model; 115 | this._implementations[cid] = implementation; 116 | 117 | this._updateLength(); 118 | 119 | return this; 120 | }, 121 | 122 | findNameByCid: function(cid) { 123 | return this._names[cid]; 124 | }, 125 | 126 | updateNameByCid: function(cid, name) { 127 | this._names[cid] = name; 128 | }, 129 | 130 | findModelByCid: function(cid) { 131 | return this._models[cid]; 132 | }, 133 | 134 | updateModelByCid: function(cid, model) { 135 | this._models[cid] = model; 136 | }, 137 | 138 | findImplementationByCid: function(cid) { 139 | return this._models[cid]; 140 | }, 141 | 142 | updateImplementationByCid: function(cid, impl) { 143 | this._implementations[cid] = impl; 144 | }, 145 | 146 | // Remove a cid 147 | remove: function(cid){ 148 | delete this._names[cid]; 149 | delete this._models[cid]; 150 | delete this._implementations[cid]; 151 | 152 | // update the length 153 | this._updateLength(); 154 | 155 | return this; 156 | }, 157 | 158 | // Fetch data of every element 159 | fetch: function() { 160 | _.each(this._models, function(model) { 161 | var cid = model.get('cid'); 162 | 163 | model.fetch({ 164 | success: function(model, response, options) { 165 | if (_.isFunction(model.parseJSON)) 166 | model.parseJSON(response); 167 | }.bind(model) 168 | }); 169 | }.bind(this)); 170 | }, 171 | 172 | // Call a method on every element in the container, 173 | // passing parameters to the call method one at a 174 | // time, like `function.call`. 175 | call: function(method){ 176 | this.apply(method, _.tail(arguments)); 177 | }, 178 | 179 | // Apply a method on every element in the container, 180 | // passing parameters to the call method one at a 181 | // time, like `function.apply`. 182 | apply: function(method, args){ 183 | _.each(this._elements, function(elem){ 184 | if (_.isFunction(elem[method])){ 185 | elem[method].apply(elem, args || []); 186 | } 187 | }); 188 | }, 189 | 190 | // Update the `.length` attribute on this container 191 | _updateLength: function(){ 192 | this.length = _.size(this._elements); 193 | } 194 | }); 195 | 196 | // Borrowing this code from Backbone.Collection: 197 | // http://backbonejs.org/docs/backbone.html#section-106 198 | // 199 | // Mix in methods from Underscore, for iteration, and other 200 | // collection related features. 201 | var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter', 202 | 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 203 | 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest', 204 | 'last', 'without', 'isEmpty', 'pluck']; 205 | 206 | _.each(methods, function(method) { 207 | Container.prototype[method] = function() { 208 | var elements = _.values(this._elements); 209 | var args = [elements].concat(_.toArray(arguments)); 210 | return _[method].apply(_, args); 211 | }; 212 | }); 213 | 214 | // return the public API 215 | return new Container(); 216 | } 217 | 218 | /* 219 | * Prototype 220 | */ 221 | 222 | // constructor 223 | Automation.prototype.super = function() { 224 | // private properties 225 | this._model = Backbone.Model.extend({}); 226 | this._count = 0; 227 | this._handlers = []; 228 | 229 | // constructor 230 | this._observer = new Automation.ChildElementContainer(); 231 | this._eventAggragator = new Automation.EventAggregator(); 232 | 233 | // notifying listing objects of state changes 234 | this._eventAggragator.on('forceUpdateAll', function() { 235 | // update every model in the container 236 | }.bind(this)); 237 | }; 238 | 239 | /** 240 | * Create a new thing with its given unique name, model 241 | * and implementation. Add `thing` to observer. 242 | * A `things` is described in JSON. 243 | * 244 | * @param {Object} name 245 | * @param {Object} model 246 | * @param {Object} implementation 247 | * @api private 248 | */ 249 | Automation.prototype._add = function(name, model, implementation) { 250 | var _model = new this._model(); 251 | 252 | // convert JSON-LD to `Backbone.Model` 253 | for(var prop in model) { 254 | if(model.hasOwnProperty(prop)) 255 | _model.set(prop, model[prop]); 256 | } 257 | 258 | // child ID is automatically increased 259 | _model.set('cid', this._count); 260 | 261 | // bind model change event 262 | _model.bind('change', function(model) { 263 | var cid = model.get('cid'); 264 | }.bind(this), _model); 265 | 266 | // push 267 | this._observer.add({ 268 | name: name, 269 | model: model, 270 | cid: this._count, 271 | implementation: implementation 272 | }); 273 | 274 | this._count++; 275 | 276 | return _model; 277 | }; 278 | 279 | Automation.prototype.trigger = function(event) { 280 | this._eventAggragator.trigger(event); 281 | }; 282 | 283 | /** 284 | * Framework APIs 285 | */ 286 | 287 | Automation.prototype.registerThing = function(thing) { 288 | return this._add(thing); 289 | }; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | 32 | var Log = require('../utils/Log'); 33 | var TAG = 'P2P/Debug'; 34 | 35 | /** 36 | * Chord Node Class 37 | */ 38 | var Node = require('../p2p'); 39 | 40 | /* 41 | * Chord protocols 42 | */ 43 | var Chord = require('../p2p/libs/message'); 44 | 45 | /** 46 | * Chord Utils 47 | */ 48 | var ChordUtils = require('../p2p/libs/utils'); 49 | 50 | /** 51 | * Web of Things Framework 52 | */ 53 | var Framework = require('../wot/framework') 54 | , WebsocketBroker = require('../wot/broker') 55 | , WebsocketRouter = require('../wot/router') 56 | , WebsocketRequestHandlers = require('../wot/requestHandlers'); 57 | 58 | /** 59 | * Util Modules 60 | */ 61 | var merge = require('utils-merge'); 62 | var uuid = require('uuid'); 63 | var util = require('util'); 64 | var WebSocketClient = require('websocket').client; 65 | 66 | /* 67 | * Chord utils 68 | */ 69 | var serialize = JSON.stringify; 70 | var deserialize = JSON.parse; 71 | 72 | 73 | /* 74 | * Blockchain system 75 | */ 76 | var Miner = require('../block/mining'); // Import flowchain miner 77 | var block = require('../block/genesis'); // Import flowchain genesis block 78 | 79 | /* 80 | * Consensus systems 81 | */ 82 | var BFT = require('../consensus'); 83 | var bft = new BFT; 84 | 85 | /** 86 | * WebSocket URL Router 87 | */ 88 | var wsHandlers = { 89 | "/object/([A-Za-z0-9-]+)/viewer": WebsocketRequestHandlers.viewer, 90 | "/object/([A-Za-z0-9-]+)/status": WebsocketRequestHandlers.status, 91 | "/object/([A-Za-z0-9-]+)/send": WebsocketRequestHandlers.send, 92 | 93 | "/node/([A-Za-z0-9-]+)/receive": WebsocketRequestHandlers.receive 94 | }; 95 | 96 | /* 97 | * Constructor - bind a Chord node 98 | * 99 | * @param {Object} Chord server 100 | */ 101 | function Server() { 102 | this.port = process.env.PORT || 8000; 103 | this.host = process.env.HOST || 'localhost'; 104 | this.endpoint = process.env.ENDPOINT || null; 105 | this.thingid = process.env.THINGID || '5550937980d51931b3000009'; 106 | this.server = null; 107 | 108 | // initialize the public attributes 109 | this.nodes = {}; 110 | 111 | // initialize the private attributes 112 | this._options = {} 113 | 114 | // Websocket connection of endpoint 115 | this.endpointConnection = null; 116 | 117 | /* 118 | * Create a unique ID for the new node. 119 | * 120 | * 1. The ID of the node can be hashed by IP address. 121 | * 2. Hased by URI at this project. 122 | */ 123 | if (process.env.ENV === 'development') 124 | var id = ChordUtils.hashTestId(process.env.ID); 125 | else 126 | var id = ChordUtils.hash(uuid.v4()); 127 | 128 | // Create a new Chord node with the ID 129 | var node = new Node(id, this); 130 | 131 | // The Node instances 132 | this.node = this.nodes[id] = node; 133 | this.last_node = id; 134 | 135 | // Create a new miner 136 | this.miner = new Miner(); 137 | this._miner_id = 0; 138 | 139 | // Blocks 140 | this.blockchain = []; 141 | }; 142 | 143 | /** 144 | * The server event handlers 145 | */ 146 | Server.prototype.onData = function(payload) { 147 | // Parse the data received from Chord node (WebSocket client) 148 | var packet = typeof payload.data === 'object' ? payload.data : deserialize(payload.data); 149 | 150 | // Request URI 151 | var pathname = payload.pathname; 152 | 153 | if (typeof packet.message === 'undefined' 154 | || typeof packet.message.type === 'undefined') { 155 | // Not a chord message 156 | if (typeof this._options.ondata === 'function') { 157 | var req = { 158 | node: {} 159 | }; 160 | var res = { 161 | save: function() {}, 162 | read: function() {} 163 | }; 164 | 165 | req.node = this.node; 166 | req.data = packet; 167 | res.save = this.save.bind(this); 168 | res.read = this.read.bind(this); 169 | 170 | return this._options.ondata(req, res); 171 | } 172 | } 173 | 174 | /* 175 | * Format of 'packet'. 176 | * 177 | * { message: { type: 0, id: '77c44c4f7bd4044129babdf235d943ff25a1d5f0' }, 178 | * from: { id: '77c44c4f7bd4044129babdf235d943ff25a1d5f0' } } 179 | */ 180 | 181 | // Get last node's instance by ID 182 | var to = this.nodes[this.last_node]; 183 | 184 | // Forward the message 185 | if (packet.to) { 186 | // Forward this message to the node ID 187 | to = this.nodes[packet.to]; 188 | } 189 | 190 | var tx = {}; 191 | 192 | if (typeof packet.message.data !== 'undefined') { 193 | tx = packet.message.data; 194 | } 195 | 196 | // Send virtual blocks to the hybrid node 197 | if (packet.message.type === Chord.NOTIFY_EDGE 198 | && typeof this._options.onedge === 'function') 199 | { 200 | var req = { 201 | node: {} 202 | }; 203 | var res = { 204 | save: function() {}, 205 | read: function() {}, 206 | send: function() {} 207 | }; 208 | 209 | req.node = this.node; 210 | req.payload = payload; 211 | req.block = this.blockchain[this.blockchain.length - 1]; 212 | req.tx = tx; 213 | 214 | res.save = this.save.bind(this); 215 | res.read = this.read.bind(this); 216 | res.send = this.sendAsset.bind(this); 217 | 218 | return this._options.onedge(req, res); 219 | } 220 | 221 | // This message is for me to query data of my blocks 222 | if (typeof this._options.onquery === 'function' 223 | && typeof tx !== 'undefined' 224 | && typeof tx.origin !== 'undefined' 225 | && packet.message.type === Chord.MESSAGE) { 226 | var req = { 227 | node: {} 228 | }; 229 | var res = { 230 | save: function() {}, 231 | read: function() {}, 232 | send: function() {} 233 | }; 234 | 235 | req.node = this.node; 236 | req.payload = payload; 237 | req.block = this.blockchain[this.blockchain.length - 1]; 238 | req.tx = tx; 239 | 240 | res.save = this.save.bind(this); 241 | res.read = this.read.bind(this); 242 | res.send = this.sendAsset.bind(this); 243 | 244 | return this._options.onquery(req, res); 245 | 246 | // The message is for me 247 | } else if (typeof this._options.onmessage === 'function' 248 | && packet.message.type === Chord.MESSAGE) { 249 | var req = { 250 | node: {} 251 | }; 252 | var res = { 253 | save: function() {}, 254 | read: function() {}, 255 | send: function() {} 256 | }; 257 | 258 | req.node = this.node; 259 | req.payload = payload; 260 | req.block = this.blockchain[this.blockchain.length - 1]; 261 | 262 | res.save = this.save.bind(this); 263 | res.read = this.read.bind(this); 264 | res.send = this.sendAsset.bind(this); 265 | 266 | return this._options.onmessage(req, res); 267 | } 268 | 269 | // Get node instance by ID and dispatch the message 270 | if (to) { 271 | to.dispatch(packet.from, packet.message); 272 | } 273 | }; 274 | 275 | /** 276 | * Web of things framework event handlers 277 | */ 278 | Server.prototype.onNewThing = function(thing) { 279 | console.debug(TAG, 'onNewThing: ' + thing); 280 | 281 | // Invoke framework API to register new thing 282 | this.registerThing(thing); 283 | }; 284 | 285 | /** 286 | * Start a Websocket server 287 | * 288 | * @param options {Object} - Configuration file 289 | * @returns {None} 290 | * @api public 291 | */ 292 | Server.prototype.start = function(options) { 293 | var self = this; 294 | var options = options || {}; 295 | 296 | for (var prop in options) { 297 | if (options.hasOwnProperty(prop) 298 | && typeof(this._options[prop]) === 'undefined') 299 | this._options[prop] = options[prop]; 300 | } 301 | 302 | // Prepare to start Websocket server 303 | var server = new WebsocketBroker({ 304 | port: this.port, 305 | host: this.host 306 | }); 307 | 308 | var router = new WebsocketRouter(); 309 | 310 | // Websocket server events (the protocol layer) 311 | server.on('data', this.onData.bind(this)); 312 | 313 | // Web of things framework event aggregation (the things layer) 314 | server.on('newThing', this.onNewThing.bind(this)); 315 | 316 | // Connect to a subsequent Chord node 317 | if (typeof options.join === 'object') { 318 | // Join the p2p network 319 | this.node.join(options.join); 320 | } 321 | 322 | server.start(router.route, wsHandlers); 323 | 324 | this.server = server; 325 | 326 | console.debug('----- Genesis Block -----'); 327 | console.debug( JSON.stringify(block) ); 328 | 329 | console.debug('----- Start virtual block mining -----'); 330 | var miner = this.miner; 331 | 332 | miner.setTransactions([this.node]); 333 | miner.setPreviousBlock(block); 334 | 335 | // Start to generate a hash 336 | this._miner_id = setInterval(function() { 337 | miner.generateHash(); 338 | 339 | // A success hash is generated 340 | if (miner.isSuccess()) { 341 | var block = miner.getNewBlock(); 342 | 343 | // Successful mined and save the new block 344 | self.blockchain.push(block); 345 | 346 | miner.setPreviousBlock(block); 347 | 348 | console.debug('Difficulty: ' + block.difficulty) 349 | console.debug('Block #' + block.no + ': ' + block.hash); 350 | } else { 351 | var block = miner.getMiningBlock(); 352 | } 353 | }, 1000); 354 | 355 | // Event callbacks 356 | if (typeof this._options.onstart === 'function') { 357 | var req = { 358 | node: {} 359 | }; 360 | var res = { 361 | save: function() {}, 362 | read: function() {} 363 | }; 364 | 365 | req.node = this.node; 366 | res.save = this.save.bind(this); 367 | res.read = this.read.bind(this); 368 | 369 | this._options.onstart(req, res); 370 | } 371 | }; 372 | 373 | /** 374 | * Shutdown the Websocket server. 375 | * 376 | * @param cb {Function} The complete callback 377 | * @return {} 378 | * @api public 379 | */ 380 | Server.prototype.shutdown = function(cb) { 381 | clearInterval(this._miner_id); 382 | this.node.clearIntervals(); 383 | if (this.server) 384 | this.server.shutdown(cb); 385 | } 386 | 387 | /* 388 | * CRUD of data query 389 | * 390 | * @param {Object} { address: '127.0.0.1', port: 8000 } 391 | * @param {Object} { type: 2, id: 'b283326930a8b2baded20bb1cf5b6358' } 392 | */ 393 | Server.prototype.save = function(data) { 394 | return this.node.save(data); 395 | }; 396 | 397 | /* 398 | * CRUD of data query 399 | * 400 | * @param key {String} - 401 | */ 402 | Server.prototype.read = function(key) { 403 | return this.node.read(key); 404 | }; 405 | 406 | /** 407 | * The RPC to send a Chord message. 408 | * 409 | * @param to {Object} - Destination. e.g. { address: '127.0.0.1', port: 8000 } 410 | * @param packet {Object} - Data payload e.g. { type: 2, id: 'b283326930a8b2baded20bb1cf5b6358' } 411 | * @returns {None} 412 | * @api public 413 | */ 414 | var connections = []; 415 | 416 | Server.prototype.sendChordMessage = function(to, packet) { 417 | var uri = util.format('ws://%s:%s/node/%s/receive', to.address, to.port, packet.message.id); 418 | var host = util.format('ws://%s:%s', to.address, to.port); 419 | var payload = { 420 | message: packet.message, 421 | from: packet.from 422 | }; 423 | 424 | // Connection cache 425 | var connection = connections[host] || null; 426 | 427 | if (ChordUtils.DebugServer) { 428 | console.debug('send to ' + uri); 429 | } 430 | 431 | if (connection) { 432 | if (connection.connected) { 433 | connection.sendUTF(JSON.stringify(payload)); 434 | } else { 435 | delete connections[host]; 436 | } 437 | 438 | return 0; 439 | } 440 | 441 | var client = new WebSocketClient(); 442 | 443 | client.on('connect', function(connection) { 444 | if (connection.connected) { 445 | connection.sendUTF(JSON.stringify(payload)); 446 | connections[host] = connection; 447 | } else { 448 | delete connections[host]; 449 | } 450 | }); 451 | 452 | client.connect(uri, ''); 453 | }; 454 | 455 | /** 456 | * Give an asset to endpoint. 457 | * 458 | * @param asset {Object} - Issued asset to be gave out 459 | * @returns {Object} 460 | * @api public 461 | */ 462 | Server.prototype.sendAsset = function(asset) { 463 | // Start forward data to endpoint 464 | var endpoint = this.endpoint; 465 | var connection = this.endpointConnection; 466 | var self = this; 467 | 468 | if (!endpoint) return; 469 | 470 | if (!/^(ws|wss):\/\//.test(endpoint)) { 471 | endpoint = 'ws://' + endpoint; 472 | } 473 | 474 | if (connection === null) { 475 | var wsClient = new WebSocketClient(); 476 | 477 | wsClient.on('connectFailed', function(error) { 478 | self.endpointConnection = null; 479 | }); 480 | 481 | wsClient.on('connect', function(conn) { 482 | conn.on('error', function(error) { 483 | self.endpointConnection = null; 484 | }); 485 | conn.on('close', function() { 486 | self.endpointConnection = null; 487 | }); 488 | conn.on('message', function(message) { 489 | }); 490 | 491 | self.endpointConnection = conn; 492 | }); 493 | 494 | var uri = util.format('%s/object/%s/send', endpoint, this.thingid); 495 | 496 | // initial websocket connection 497 | return wsClient.connect(uri, ''); 498 | } 499 | 500 | if (typeof asset === 'object') 501 | asset = JSON.stringify(asset); 502 | 503 | // Send data to endpont 504 | connection.sendUTF(asset); 505 | }; 506 | 507 | /** 508 | * Create a web of things server 509 | * 510 | * @returns {Object} 511 | * @api public 512 | */ 513 | function createServer() { 514 | var _server = new Server(); 515 | 516 | // Combined Websocket server with web of things framework 517 | var server = new Framework({ 518 | server: _server 519 | }); 520 | 521 | return server; 522 | } 523 | 524 | /** 525 | * Export the server. 526 | */ 527 | module.exports = createServer(); 528 | -------------------------------------------------------------------------------- /p2p/libs/node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The MIT License (MIT) 4 | * 5 | * https://www.flowchain.co 6 | * 7 | * Copyright (c) 2016-present Jollen 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var Log = require('../../utils/Log'); 32 | var TAG = 'P2P/Chord'; 33 | 34 | var ChordUtils = require('./utils'); 35 | require('console.table'); 36 | 37 | // Chord protocols 38 | var Chord = require('./message'); 39 | 40 | function Node(id, server) { 41 | this.id = id; 42 | this.address = server.host; 43 | this.port = server.port; 44 | 45 | this.server = server; 46 | 47 | // Each node can keep a finger table containing up to 'm' entries 48 | // Default is 32 entries 49 | this.finger_entries = 8; 50 | this.ttl = 6; 51 | 52 | // Default successor is self 53 | this._self = { 54 | address: this.address, 55 | port: this.port, 56 | id: this.id 57 | }; 58 | 59 | // Create a new Chord ring 60 | this.predecessor = null; 61 | this.successor = this._self; 62 | 63 | this.hybrid = { 64 | address: process.env.EDGE_HOST || 'localhost', 65 | port: process.env.EDGE_PORT || '8000' 66 | }; 67 | 68 | // Initialize finger table 69 | this.fingers = []; 70 | this.fingers.length = 0; 71 | 72 | this.next_finger = 0; 73 | 74 | // TTL of predecessor 75 | this.predecessor_ttl = this.ttl; 76 | this.successor_ttl = this.ttl; 77 | 78 | console.debug(TAG, 'node id = '+ this.id); 79 | console.debug(TAG, 'successor = ' + JSON.stringify(this.successor)); 80 | 81 | this._startUpdateFingers(); 82 | }; 83 | 84 | /* 85 | * Fix finger table entries. 86 | */ 87 | Node.prototype._startUpdateFingers = function() { 88 | var fix_fingers = function() { 89 | var fixFingerId = ''; 90 | var next = this.next_finger; 91 | 92 | if (next >= this.finger_entries) { 93 | next = 0; 94 | } 95 | 96 | fixFingerId = ChordUtils.getFixFingerId(this.id, next); 97 | this.next_finger = next + 1; 98 | 99 | if (ChordUtils.DebugFixFingers) { 100 | console.info(TAG, 'getFixFingerId = ' + fixFingerId); 101 | console.info(TAG, 'finger table length = '+ this.fingers.length); 102 | } 103 | 104 | // n.fix_fingers() 105 | this.send(this._self, { 106 | type: Chord.FIND_SUCCESSOR, 107 | id: fixFingerId, 108 | next: next 109 | }); 110 | 111 | // Print finger table, predecessor and successor 112 | if (ChordUtils.DebugPrintFingerTable) { 113 | var dataset = []; 114 | 115 | for (var i = this.fingers.length - 1; i >= 0; --i) { 116 | dataset.push({ 117 | next: i, 118 | key: this.fingers[i].key, 119 | successor: this.fingers[i].successor.id 120 | }); 121 | } 122 | console.table(dataset); 123 | 124 | console.debug(TAG, 'successor: ' + JSON.stringify(this.successor)); 125 | console.debug(TAG, 'predecessor: ' + JSON.stringify(this.predecessor)); 126 | 127 | // send to debug server 128 | this.send({ 129 | address: process.env.DEBUGSERVER || 'localhost', 130 | port: process.env.DEBUGSERVER_PORT || 9000 131 | }, { 132 | id: '00000000', 133 | node: this.id, 134 | successor: this.successor, 135 | predecessor: this.predecessor 136 | }); 137 | } 138 | }; 139 | 140 | // Stabilize 141 | this._stabilize = setInterval(function stabilize() { 142 | this.send(this.successor, { type: Chord.NOTIFY_STABILIZE }); 143 | }.bind(this), 60000); 144 | 145 | // Failure check 146 | this._check_predecessor = setInterval(function check_predecessor() { 147 | if (ChordUtils.DebugFailureCheck) { 148 | console.info(TAG, 'predecessor_ttl =', this.predecessor_ttl); 149 | } 150 | 151 | // check predecessor 152 | if (--this.predecessor_ttl < 1) { 153 | this.predecessor = null; 154 | this.predecessor_ttl = this.ttl; 155 | } 156 | 157 | // checks whether predecessor has failed 158 | if (this.predecessor !== null) 159 | this.send(this.predecessor, { type: Chord.CHECK_PREDECESSOR, predecessor_ttl: this.predecessor_ttl }); 160 | }.bind(this), 30000); 161 | 162 | // Failure check 163 | this._check_successor = setInterval(function check_successor() { 164 | if (ChordUtils.DebugFailureCheck) { 165 | console.info(TAG, 'successor_ttl = ' + this.successor_ttl); 166 | } 167 | 168 | // check successor 169 | if (--this.successor_ttl < 1) { 170 | this.successor = this._self; 171 | this.successor_ttl = this.ttl; 172 | } 173 | 174 | this.send(this.successor, { type: Chord.CHECK_SUCESSOR, successor_ttl: this.successor_ttl }); 175 | }.bind(this), 30000); 176 | 177 | this._fix_fingers = setInterval(fix_fingers.bind(this), 15000); 178 | } 179 | 180 | Node.prototype.clearIntervals = function() { 181 | clearInterval(this._fix_fingers); 182 | clearInterval(this._check_successor); 183 | clearInterval(this._check_predecessor); 184 | clearInterval(this._stabilize); 185 | } 186 | 187 | /* 188 | * @param {Object} { address: '127.0.0.1', port: 8000 } 189 | * @param {Object} { type: 2, id: 'b283326930a8b2baded20bb1cf5b6358' } 190 | */ 191 | Node.prototype.send = function(from, message, to) { 192 | if (typeof to === 'undefined') { 193 | to = from; 194 | from = this._self; 195 | } 196 | 197 | if (typeof message.id === 'undefined') { 198 | message.id = this.id; 199 | } 200 | 201 | var packet = { 202 | from: { 203 | address: from.address, 204 | port: from.port, 205 | id: from.id 206 | }, 207 | message: message 208 | }; 209 | 210 | return this.server.sendChordMessage(to, packet); 211 | }; 212 | 213 | /* 214 | * Save data to successor(key) 215 | */ 216 | Node.prototype.save = function(data) { 217 | var to = this.successor; 218 | var from = this._self; 219 | var key = ChordUtils.hash(data); 220 | 221 | var message = { 222 | id: key, 223 | type: Chord.FIND_SUCCESSOR, 224 | data: data 225 | }; 226 | 227 | this.send(to, message); 228 | 229 | return true; 230 | }; 231 | 232 | /* 233 | * Read data of key from successor(key) 234 | */ 235 | Node.prototype.read = function(key) { 236 | var to = this.successor; 237 | var from = this._self; 238 | 239 | var message = { 240 | id: key, 241 | type: Chord.FIND_SUCCESSOR, 242 | data: { 243 | origin: from, 244 | key: key 245 | } 246 | }; 247 | 248 | this.send(to, message); 249 | 250 | return true; 251 | }; 252 | 253 | /* 254 | * @return {boolean} 255 | */ 256 | Node.prototype.join = function(remote) { 257 | var message = { 258 | type: Chord.NOTIFY_JOIN 259 | }; 260 | 261 | this.predecessor = null; 262 | 263 | if (ChordUtils.DebugNodeJoin) 264 | console.info(TAG, 'try to join ' + JSON.stringify(remote)); 265 | 266 | // Join 267 | this.send(remote, message); 268 | 269 | return true; 270 | }; 271 | 272 | Node.prototype.submitVirtualBlocks = function(virtual_blocks) { 273 | var to = this.hybrid; 274 | var key = ChordUtils.hash(virtual_blocks); 275 | 276 | var message = { 277 | id: key, 278 | type: Chord.NOTIFY_EDGE, 279 | data: virtual_blocks, 280 | }; 281 | 282 | this.send(to, message); 283 | 284 | return true; 285 | }; 286 | 287 | /* 288 | * Return closet finger proceding ID 289 | */ 290 | Node.prototype.closet_finger_preceding = function(find_id) { 291 | /* 292 | * n.closest_preceding_node(id) 293 | * for i = m downto 1 294 | * if (finger[i]∈(n,id)) 295 | * return finger[i]; 296 | * return n; 297 | */ 298 | for (var i = this.fingers.length - 1; i >= 0; --i) { 299 | if (this.fingers[i] && ChordUtils.isInRange(this.fingers[i].successor.id, this.id, find_id)) { 300 | return this.fingers[i].successor; 301 | } 302 | } 303 | 304 | if (ChordUtils.isInRange(this.successor.id, this.id, find_id)) { 305 | return this.successor; 306 | } 307 | 308 | return this._self; 309 | }; 310 | 311 | Node.prototype.dispatch = function(_from, _message) { 312 | var from = _from; 313 | var message = _message; 314 | 315 | switch (message.type) { 316 | // N notifies its successor for predecessor 317 | case Chord.NOTIFY_STABILIZE: 318 | /* 319 | * n.stabilize() 320 | * x = successor.predecessor; 321 | * if (x∈(n, successor)) 322 | * successor = x; 323 | * successor.notify(n); 324 | */ 325 | if (ChordUtils.DebugStabilize) 326 | console.info(TAG, 'NOTIFY_STABILIZE: from = ' + from.id + ', this = ' + this.id + ', this.successor = ' + this.successor.id); 327 | 328 | // N might be our predecessor 329 | if (this.predecessor === null) { 330 | this.predecessor = from; 331 | } 332 | 333 | // unstabilized 334 | if (ChordUtils.isInRange(this.predecessor.id, from.id, this.id)) { 335 | message.type = Chord.NOTIFY_PREDECESSOR; 336 | return this.send(this.predecessor, message, from); 337 | } 338 | 339 | message.type = Chord.NOTIFY_SUCCESSOR; 340 | this.send(from, message, this); 341 | 342 | break; 343 | 344 | case Chord.NOTIFY_PREDECESSOR: 345 | if (ChordUtils.DebugStabilize) 346 | console.info(TAG, 'NOTIFY_PREDECESSOR: from =', from.id, ', this =', this.id, ', this.successor =', this.successor.id); 347 | 348 | if (ChordUtils.isInRange(from.id, this.id, this.successor.id)) { 349 | this.successor = from; 350 | 351 | if (ChordUtils.DebugStabilize) 352 | console.info(TAG, 'NOTIFY_PREDECESSOR: new successor is now = ' + this.successor.id); 353 | } 354 | 355 | message.type = Chord.NOTIFY_SUCCESSOR; 356 | 357 | this.send(this, message, this.successor); 358 | 359 | break; 360 | 361 | case Chord.NOTIFY_SUCCESSOR: 362 | if (ChordUtils.DebugStabilize) 363 | console.info(TAG, 'NOTIFY_SUCCESSOR: from =', from.id, ', this =', this.id, ', this.successor =', this.successor.id); 364 | 365 | /* n.notify(n') 366 | * if (predecessor is nil or n'∈(predecessor, n)) 367 | * predecessor = n'; 368 | */ 369 | if (this.predecessor === null 370 | || ChordUtils.isInRange(from.id, this.predecessor.id, this.id)) { 371 | this.predecessor = from; 372 | 373 | if (ChordUtils.DebugStabilize) 374 | console.info(TAG, 'NOTIFY_SUCCESSOR: new predecessor is now = ' + this.predecessor.id); 375 | } 376 | 377 | break; 378 | 379 | case Chord.FOUND_SUCCESSOR: 380 | // fix finger table 381 | if (message.hasOwnProperty('next')) { 382 | this.fingers[message.next] = { 383 | successor: from, 384 | key: message.id 385 | }; 386 | 387 | if (ChordUtils.DebugSuccessor) 388 | console.info(TAG, 'FOUND_SUCCESSOR = finger table fixed'); 389 | 390 | // find successor(key) 391 | } else if (message.hasOwnProperty('data')) { 392 | if (ChordUtils.DebugSuccessor) 393 | console.info(TAG, 'found successor(key) = ' + message.id); 394 | 395 | message.type = Chord.MESSAGE; 396 | this.send(this, message, from); 397 | 398 | // find successor(n) 399 | } else { 400 | this.successor = from; 401 | 402 | if (ChordUtils.DebugSuccessor) 403 | console.info(TAG, 'new successor is now = ' + this.successor.id); 404 | } 405 | 406 | break; 407 | 408 | case Chord.NOTIFY_JOIN: 409 | console.info(TAG, 'Node joined: ' + JSON.stringify(from)); 410 | 411 | case Chord.FIND_SUCCESSOR: 412 | if (ChordUtils.DebugNodeJoin || ChordUtils.DebugSuccessor) 413 | console.info(TAG, 'FIND_SUCCESSOR: from =', from.id, ', this =', this.id, ', this.successor =', this.successor.id, ', message.id =', message.id); 414 | 415 | // Yes, that should be a closing square bracket to match the opening parenthesis. 416 | // It is a half closed interval. 417 | if (ChordUtils.isInHalfRange(message.id, this.id, this.successor.id)) { 418 | if (ChordUtils.DebugSuccessor) 419 | console.info(TAG, 'FIND_SUCCESSOR = ' + this.successor.id); 420 | 421 | message.type = Chord.FOUND_SUCCESSOR; 422 | this.send(this.successor, message, from); 423 | 424 | // Fix finger table, find successor(key) or read(key) 425 | } else if (message.hasOwnProperty('next') || message.hasOwnProperty('data')) { 426 | var n0 = this.closet_finger_preceding(message.id); 427 | 428 | if (ChordUtils.DebugSuccessor) 429 | console.info(TAG, 'FIND_SUCCESSOR = closet_finger_preceding = ' + n0.id); 430 | 431 | message.type = Chord.FOUND_SUCCESSOR; 432 | this.send(n0, message, from); 433 | 434 | // Forward the query around the circle 435 | } else { 436 | var n0 = this.closet_finger_preceding(message.id); 437 | 438 | if (ChordUtils.DebugSuccessor) 439 | console.info(TAG, 'Forward to =', from.id, ', this =', this.id, ', this.successor =', this.successor.id, ', message.id =', message.id, ', n0 =' + n0.id); 440 | 441 | message.id = n0.id; 442 | this.send(n0, message, from); 443 | } 444 | 445 | break; 446 | 447 | case Chord.CHECK_PREDECESSOR: 448 | // reset our ttl 449 | message.type = Chord.CHECK_TTL; 450 | message.predecessor_ttl = this.ttl; 451 | 452 | this.send(this, message, from); 453 | 454 | break; 455 | 456 | case Chord.CHECK_SUCESSOR: 457 | // reset our ttl 458 | message.type = Chord.CHECK_TTL; 459 | message.successor_ttl = this.ttl; 460 | 461 | this.send(this, message, from); 462 | 463 | break; 464 | 465 | case Chord.CHECK_TTL: 466 | if (message.hasOwnProperty('predecessor_ttl')) { 467 | this.predecessor_ttl = message.predecessor_ttl; 468 | } 469 | 470 | if (message.hasOwnProperty('successor_ttl')) { 471 | this.successor_ttl = message.successor_ttl; 472 | } 473 | 474 | break; 475 | 476 | case Chord.NOTIFY_EDGE: 477 | 478 | // find successor(key) 479 | case Chord.MESSAGE: 480 | if (ChordUtils.DebugMessage) 481 | console.info(TAG, 'Message from', from,' =', message); 482 | 483 | break; 484 | 485 | default: 486 | Log.i(TAG, 'Unknown Chord message = ' + message.type); 487 | break; 488 | }; 489 | }; 490 | 491 | /* 492 | * Export 'Node' class 493 | */ 494 | if (typeof(module) != "undefined" && typeof(exports) != "undefined") 495 | module.exports = Node; 496 | --------------------------------------------------------------------------------