├── .gitignore ├── LICENSE ├── README.md ├── backend ├── .eslintignore ├── .eslintrc ├── .gitignore ├── app.js ├── batch │ ├── batch_config.js │ └── index.js ├── bin │ └── www ├── config │ ├── developer.json │ └── development.json ├── deploy │ ├── eco_developer.json │ └── eco_development.json ├── doc │ └── mongo.txt ├── external_apis │ └── eos_api.js ├── libs │ ├── api │ │ ├── committee.js │ │ ├── proposal.js │ │ ├── proposer.js │ │ ├── stats.js │ │ └── user.js │ ├── batch │ │ ├── process_actions │ │ │ ├── committee │ │ │ │ └── index.js │ │ │ ├── delegate │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ ├── proposal │ │ │ │ └── index.js │ │ │ ├── proposer │ │ │ │ └── index.js │ │ │ ├── reviewer │ │ │ │ └── index.js │ │ │ ├── vote │ │ │ │ └── index.js │ │ │ └── watchman │ │ │ │ └── index.js │ │ ├── update_proposals │ │ │ └── index.js │ │ ├── update_summary │ │ │ └── index.js │ │ └── update_votings │ │ │ ├── check_voting.js │ │ │ ├── index.js │ │ │ ├── stakeunstake.js │ │ │ ├── unvote.js │ │ │ └── vote.js │ ├── enum.js │ ├── error.js │ ├── log.js │ └── util.js ├── log │ └── .gitignore ├── models │ ├── mongo │ │ ├── index.js │ │ ├── lib_action.js │ │ ├── lib_action_stake.js │ │ ├── lib_action_unstake.js │ │ ├── lib_committee.js │ │ ├── lib_proposal.js │ │ ├── lib_proposer.js │ │ ├── lib_reviewer.js │ │ ├── lib_summary.js │ │ ├── lib_update_proposal.js │ │ ├── lib_update_voting.js │ │ ├── lib_voter_info.js │ │ ├── lib_voting_info.js │ │ ├── user.js │ │ └── wps_info.js │ └── redis │ │ ├── index.js │ │ └── redis_client.js ├── package.json ├── readme.md ├── routes │ └── api │ │ ├── committee.js │ │ ├── index.js │ │ ├── proposal.js │ │ ├── proposer.js │ │ ├── stats.js │ │ └── user.js ├── scripts │ ├── cleos.sh │ └── cleos_dev.sh ├── test │ ├── batch │ │ ├── index.js │ │ ├── update_proposals │ │ │ └── index.js │ │ ├── update_summary │ │ │ └── index.js │ │ └── update_votings │ │ │ ├── _index.js │ │ │ ├── check_voting.js │ │ │ ├── index.js │ │ │ ├── stakeunstake.js │ │ │ ├── unvote.js │ │ │ └── vote.js │ ├── data │ │ ├── accounts.js │ │ └── index.js │ ├── external_apis │ │ └── index.js │ ├── libs │ │ └── index.js │ └── routes │ │ └── api │ │ ├── committee.js │ │ ├── index.js │ │ ├── proposal.js │ │ ├── proposer.js │ │ ├── stats.js │ │ └── user.js └── tools │ └── create_wps_info.js └── contracts ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── UnitTestsExternalProject.txt ├── build.sh ├── eosio.wps ├── CMakeLists.txt ├── README.md ├── abi │ ├── eosio.wps-acceptprop-rc.md │ ├── eosio.wps-approve-rc.md │ ├── eosio.wps-checkvote-rc.md │ ├── eosio.wps-editproposer-rc.md │ ├── eosio.wps-editreviewer-rc.md │ ├── eosio.wps-regcommittee-rc.md │ ├── eosio.wps-regproposer-rc.md │ ├── eosio.wps-regreviewer-rc.md │ ├── eosio.wps-rejectfund-rc.md │ ├── eosio.wps-rejectprop-rc.md │ ├── eosio.wps-rmvreviewer-rc.md │ ├── eosio.wps-unvote-rc.md │ ├── eosio.wps-vote-rc.md │ └── eosio.wps.abi ├── bin │ └── eosio.wps │ │ └── .gitignore ├── build.sh ├── include │ ├── .DS_Store │ └── eosio.wps │ │ ├── committee.hpp │ │ ├── eosio.wps.hpp │ │ ├── proposal.hpp │ │ ├── proposer.hpp │ │ └── reviewer.hpp └── src │ ├── committee.cpp │ ├── eosio.wps.cpp │ ├── proposal.cpp │ ├── proposer.cpp │ ├── reviewer.cpp │ ├── vote.cpp │ └── watchman.cpp └── tests ├── CMakeLists.txt ├── contracts.hpp ├── contracts.hpp.in ├── main.cpp ├── test.hpp ├── test_committee.cpp ├── test_contracts ├── eosio.token.abi ├── eosio.token.wasm ├── exchange.wasm ├── exchange.wast ├── test_api.wasm └── test_api.wast ├── test_proposal.cpp ├── test_proposer.cpp ├── test_reviewer.cpp └── test_wps.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Worker Proposal System - Backend 2 | -------------------------------------------------------------------------------- /backend/.eslintignore: -------------------------------------------------------------------------------- 1 | views/ 2 | log/ 3 | -------------------------------------------------------------------------------- /backend/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | es6: true 4 | mocha: true 5 | 6 | parserOptions: 7 | ecmaVersion: 8 8 | ecmaFeatures: 9 | experimentalObjectRestSpread: true 10 | 11 | rules: 12 | # Possible Errors 13 | # http://eslint.org/docs/rules/#possible-errors 14 | comma-dangle: [2, "only-multiline"] 15 | no-control-regex: 2 16 | no-debugger: 2 17 | no-dupe-args: 2 18 | no-dupe-keys: 2 19 | no-duplicate-case: 2 20 | no-empty-character-class: 2 21 | no-ex-assign: 2 22 | no-extra-boolean-cast: 2 23 | no-extra-parens: [2, "functions"] 24 | no-extra-semi: 2 25 | no-func-assign: 2 26 | no-invalid-regexp: 2 27 | no-irregular-whitespace: 2 28 | no-negated-in-lhs: 2 29 | no-obj-calls: 2 30 | no-proto: 2 31 | no-unexpected-multiline: 2 32 | no-unreachable: 2 33 | use-isnan: 2 34 | valid-typeof: 2 35 | 36 | # Best Practices 37 | # http://eslint.org/docs/rules/#best-practices 38 | no-fallthrough: 2 39 | no-octal: 2 40 | no-redeclare: 2 41 | no-self-assign: 2 42 | no-unused-labels: 2 43 | 44 | # Strict Mode 45 | # http://eslint.org/docs/rules/#strict-mode 46 | strict: [2, "global"] 47 | 48 | # Variables 49 | # http://eslint.org/docs/rules/#variables 50 | no-delete-var: 2 51 | no-undef: 2 52 | no-unused-vars: [2, {"args": "none"}] 53 | 54 | # Node.js and CommonJS 55 | # http://eslint.org/docs/rules/#nodejs-and-commonjs 56 | no-mixed-requires: 2 57 | no-new-require: 2 58 | no-path-concat: 2 59 | no-restricted-modules: [2, "sys", "_linklist"] 60 | 61 | # Stylistic Issues 62 | # http://eslint.org/docs/rules/#stylistic-issues 63 | comma-spacing: 2 64 | eol-last: 2 65 | indent: [2, 4, {SwitchCase: 1, "MemberExpression": 'off'}] 66 | keyword-spacing: 2 67 | max-len: [2, 300, 2] 68 | new-parens: 2 69 | no-mixed-spaces-and-tabs: 2 70 | no-multiple-empty-lines: [2, {max: 2}] 71 | no-trailing-spaces: 2 72 | quotes: [2, "single", "avoid-escape"] 73 | semi: 2 74 | space-before-blocks: [2, "always"] 75 | space-before-function-paren: [2, "never"] 76 | space-in-parens: [2, "never"] 77 | space-infix-ops: 2 78 | space-unary-ops: 2 79 | 80 | # ECMAScript 6 81 | # http://eslint.org/docs/rules/#ecmascript-6 82 | arrow-parens: [2, "always"] 83 | arrow-spacing: [2, {"before": true, "after": true}] 84 | constructor-super: 2 85 | no-class-assign: 2 86 | no-confusing-arrow: 2 87 | no-const-assign: 2 88 | no-dupe-class-members: 2 89 | no-new-symbol: 2 90 | no-this-before-super: 2 91 | prefer-const: 0 92 | 93 | # Custom rules in tools/eslint-rules 94 | align-function-arguments: 0 95 | align-multiline-assignment: 0 96 | assert-fail-single-argument: 0 97 | # new-with-error: [2, "Error", "RangeError", "TypeError", "SyntaxError", "ReferenceError"] 98 | # no-deepEqual: 1 99 | 100 | # Global scoped method and vars 101 | globals: 102 | DTRACE_HTTP_CLIENT_REQUEST : false 103 | LTTNG_HTTP_CLIENT_REQUEST : false 104 | COUNTER_HTTP_CLIENT_REQUEST : false 105 | DTRACE_HTTP_CLIENT_RESPONSE : false 106 | LTTNG_HTTP_CLIENT_RESPONSE : false 107 | COUNTER_HTTP_CLIENT_RESPONSE : false 108 | DTRACE_HTTP_SERVER_REQUEST : false 109 | LTTNG_HTTP_SERVER_REQUEST : false 110 | COUNTER_HTTP_SERVER_REQUEST : false 111 | DTRACE_HTTP_SERVER_RESPONSE : false 112 | LTTNG_HTTP_SERVER_RESPONSE : false 113 | COUNTER_HTTP_SERVER_RESPONSE : false 114 | DTRACE_NET_STREAM_END : false 115 | LTTNG_NET_STREAM_END : false 116 | COUNTER_NET_SERVER_CONNECTION_CLOSE : false 117 | DTRACE_NET_SERVER_CONNECTION : false 118 | LTTNG_NET_SERVER_CONNECTION : false 119 | COUNTER_NET_SERVER_CONNECTION : false -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .DS_Store 4 | !/log/dummy 5 | public/images/ 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let express = require('express'), 4 | path = require('path'), 5 | logger = require('morgan'), 6 | cookieParser = require('cookie-parser'), 7 | bodyParser = require('body-parser'), 8 | cors = require('cors'), 9 | config = require('config'), 10 | log = require('libs/log'), 11 | routeApi = require('routes/api'); 12 | 13 | let errLog = log.errLog; 14 | let app = express(); 15 | 16 | // view engine setup 17 | app.set('views', path.join(__dirname, 'views')); 18 | app.set('view engine', 'pug'); 19 | 20 | // nginx를 사용하는 경우, 실제 clientIP를 얻기 위해서 21 | app.enable('trust proxy'); 22 | 23 | app.use(logger(':remote-addr - ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" - ":response-time ms"', {stream : log.webLogStream})); 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | app.use(bodyParser.json({limit: '30mb'})); 26 | app.use(bodyParser.urlencoded({limit: '30mb', extended: true})); 27 | app.use(cookieParser()); 28 | 29 | app.use(cors({ 30 | origin : config.cors.whitelist, 31 | methods : ['GET', 'POST', 'DELETE', 'PUT'], 32 | credentials : true, 33 | })); 34 | 35 | routeApi.useRoutes(app); 36 | 37 | // catch 404 and forwarding to error handler 38 | app.use(function(req, res, next) { 39 | res.status(500).json({error : 'NOT_FOUND'}); 40 | }); 41 | 42 | app.use(function errorHandler(err, req, res, next) { 43 | errLog.info(err); 44 | errLog.info(err.stack); 45 | res.status(500).json({error : 'INTERNAL_SERVER_ERROR'}); 46 | }); 47 | 48 | process.on('uncaughtException', function(err) { 49 | errLog.info('uncaughtException : ' + err); 50 | errLog.info(err.stack); 51 | }); 52 | 53 | module.exports = app; 54 | -------------------------------------------------------------------------------- /backend/batch/batch_config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'); 4 | 5 | module.exports = [ 6 | { 7 | /* 8 | job_group : 'xxx_log', 9 | jobs : [] 10 | */ 11 | }, { 12 | job_group : 'libBlock', 13 | jobs : [ 14 | {name : 'processActions', params : {}, schedule : {second : _.range(1, 60, 1)}, excludes : ['']}, 15 | {name : 'updateProposals', params : {}, schedule : {second : _.range(1, 60, 1)}, excludes : ['']}, 16 | {name : 'updateVotings', params : {}, schedule : {second : _.range(1, 60, 1)}, excludes : ['']}, 17 | {name : 'updateSummary', params : {}, schedule : {second : _.range(1, 60, 1)}, excludes : ['']}, 18 | ] 19 | } 20 | ]; 21 | -------------------------------------------------------------------------------- /backend/batch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let _ = require('lodash'), 4 | batchConfig = require('batch/batch_config'); 5 | 6 | let jobGroup = process.env.NODE_JOB_GROUP, 7 | arrJob = []; 8 | 9 | if (!_.isEmpty(jobGroup)) { 10 | let arrJobGroup = jobGroup.split('|'); 11 | _.forEach(arrJobGroup, (group) => { 12 | group = _.trim(group); 13 | let batch = _.find(batchConfig, {job_group: group}); 14 | if (!_.isEmpty(batch)) { 15 | arrJob = _.union(arrJob, batch.jobs); 16 | } 17 | }); 18 | 19 | arrJob = _.map(arrJob, function(job) { 20 | if (_.indexOf(job.excludes, process.env.NODE_ENV) >= 0) { 21 | console.log('exclude :' + job.name); 22 | return; 23 | } 24 | return job; 25 | }); 26 | arrJob = _.compact(arrJob); 27 | } 28 | 29 | if (_.isEmpty(arrJob)) { 30 | module.exports = null; 31 | return ; 32 | } 33 | 34 | let Promise = require('bluebird'), 35 | schedule = require('node-schedule'), 36 | moment = require('moment'), 37 | RedisClient = require('models/redis'), 38 | log = require('libs/log'), 39 | SEnum = require('libs/enum'); 40 | 41 | const errLog = log.errLog; 42 | 43 | let isRegistered = false, 44 | arrBatchJob = []; 45 | 46 | function wrapFunc(funcPro, params, jobName) { 47 | let isRunning = false; 48 | return function() { 49 | let startedAt = moment.utc(); 50 | if (!isRunning) { 51 | let batchCmd = new RedisClient(SEnum.REDIS_TYPE_BATCH_CMD, [jobName]); 52 | let batchCmdAll = new RedisClient(SEnum.REDIS_TYPE_BATCH_CMD_ALL, []); 53 | let batchStatus = new RedisClient(SEnum.REDIS_TYPE_BATCH_STATUS, [jobName]); 54 | 55 | return Promise.join( 56 | batchCmd.restoreAsync(), 57 | batchCmdAll.restoreAsync(), 58 | function(cmd, cmdAll) { 59 | if (cmd == 'PAUSE' || cmdAll === 'PAUSE') { 60 | throw new Error('PAUSE'); 61 | } 62 | isRunning = true; 63 | batchStatus.value = JSON.stringify({status : 'RUN', date : startedAt.format('YYYY-MM-DD HH:mm:ssZ')}); 64 | return batchStatus.saveAsync(); 65 | }) 66 | .then(function() { 67 | return funcPro(params); 68 | }) 69 | .then(function(result) { 70 | let endedAt = moment.utc(); 71 | isRunning = false; 72 | 73 | batchStatus.value = JSON.stringify({status : 'END', date : endedAt.format('YYYY-MM-DD HH:mm:ssZ')}); 74 | return batchStatus.saveAsync(); 75 | }) 76 | .catch(function(err) { 77 | isRunning = false; 78 | if (err.message == 'PAUSE') { 79 | batchStatus.value = JSON.stringify({status : 'PAUSE'}); 80 | return batchStatus.saveAsync(); 81 | } else { 82 | errLog.info(err); 83 | errLog.info(err.stack); 84 | /* 85 | let batchJob = _.find(arrBatchJob, {name : jobName}); 86 | if (!_.isEmpty(batchJob)) { 87 | batchJob.job.cancel(); 88 | isRunning = false; 89 | } 90 | */ 91 | } 92 | }); 93 | } 94 | }; 95 | } 96 | 97 | let registerJobs = function() { 98 | if (isRegistered === true) { 99 | return isRegistered; 100 | } 101 | const processActions = require('libs/batch/process_actions'); 102 | const updateProposals = require('libs/batch/update_proposals'); 103 | const updateVotings = require('libs/batch/update_votings'); 104 | const updateSummary = require('libs/batch/update_summary'); 105 | let funcMap = _.extend(processActions, updateProposals, updateVotings, updateSummary); 106 | return Promise.map(arrJob, function(job) { 107 | let rule = new schedule.RecurrenceRule(), 108 | jobName = job.name, 109 | scheduleInfo = job.schedule; 110 | 111 | if (!_.isNil(scheduleInfo.hour)) { 112 | rule.hour = scheduleInfo.hour; 113 | } 114 | if (!_.isNil(scheduleInfo.minute)) { 115 | rule.minute = scheduleInfo.minute; 116 | } 117 | if (!_.isNil(scheduleInfo.second)) { 118 | rule.second = scheduleInfo.second; 119 | } 120 | 121 | if (!_.isEmpty(jobName)) { 122 | let func = wrapFunc(funcMap[jobName], job.params, jobName), 123 | j = null; 124 | 125 | if (!_.isNil(func)) { 126 | let batchCmd = new RedisClient(SEnum.REDIS_TYPE_BATCH_CMD, [jobName]); 127 | let batchCmdAll = new RedisClient(SEnum.REDIS_TYPE_BATCH_CMD_ALL, []); 128 | 129 | j = schedule.scheduleJob(rule, func); 130 | arrBatchJob.push({job : j, name : jobName}); 131 | 132 | batchCmd.value = 'START'; 133 | batchCmdAll.value = 'START'; 134 | return Promise.join( 135 | batchCmd.saveAsync(), 136 | batchCmdAll.saveAsync(), 137 | function(result1, result2) { 138 | 139 | }); 140 | } 141 | } 142 | }) 143 | .then(function() { 144 | isRegistered = true; 145 | return isRegistered; 146 | }); 147 | 148 | }; 149 | 150 | let unregisterJobs = function() { 151 | _.forEach(arrBatchJob, (batchJob) => batchJob.job.cancel()); 152 | arrBatchJob = []; 153 | isRegistered = false; 154 | }; 155 | 156 | let isRegisteredJobs = function() { 157 | return isRegistered; 158 | }; 159 | 160 | module.exports = { 161 | registerJobs : registerJobs, 162 | unregisterJobs : unregisterJobs, 163 | isRegisteredJobs : isRegisteredJobs, 164 | }; 165 | -------------------------------------------------------------------------------- /backend/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const http = require('http'), 6 | _ = require('lodash'), 7 | app = require('app'), 8 | log = require('libs/log'), 9 | batch = require('batch/index'); 10 | 11 | const port = normalizePort(process.env.PORT || '3000'); 12 | app.set('port', port); 13 | 14 | const server = http.createServer(app); 15 | 16 | server.listen(port); 17 | server.on('error', (err) => { 18 | if (err.syscall !== 'listen') { 19 | log.errLog.info(JSON.stringify(err)); 20 | throw err; 21 | } 22 | err.bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; 23 | log.errLog.info(JSON.stringify(err)); 24 | }); 25 | 26 | server.on('listening', () => { 27 | if (!_.isEmpty(batch)) { 28 | batch.registerJobs(); 29 | } 30 | const addr = server.address(); 31 | const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; 32 | console.log('Listening on ' + bind); 33 | }); 34 | 35 | function normalizePort(val) { 36 | const port = parseInt(val, 10); 37 | if (isNaN(port)) { // named pipe 38 | return val; 39 | } 40 | if (port >= 0) { // port number 41 | return port; 42 | } 43 | return false; 44 | } 45 | -------------------------------------------------------------------------------- /backend/config/developer.json: -------------------------------------------------------------------------------- 1 | { 2 | "timezone" : "+00:00", 3 | "mongo" : { 4 | "host" : "localhost:27017", 5 | "dbName" : "devwps", 6 | "account" : "devwps", 7 | "passwd" : "devwps123!" 8 | }, 9 | "redis" : { 10 | "hosts" : "localhost", //["localhost", "localhost:16379", "localhost:26379"], 11 | "port" : 6379, 12 | "expire" : { 13 | "default" : 86400 14 | } 15 | }, 16 | "eosNode" : { // connect to eos node 17 | "host" :"127.0.0.1", 18 | "protocol": "http", 19 | "httpPort" : 8888, 20 | "systemAccount" : "eosio", 21 | "wpsAccount" : "eosio.wps", 22 | "tokenAccount" : "eosio.token", 23 | "symbol" : "EOS", 24 | "chainId" : "a4fe43c897279a677025624555f3835b949f995f87923c97f0392dcff835bfd0" 25 | }, 26 | "webLog" : { 27 | "level" : "info", 28 | "filename" : "./log/web.log", // you must make log folder 29 | "datepattern" : ".yyyy-MM-dd" 30 | }, 31 | "dbLog" : { 32 | "level" : "info", 33 | "filename" : "./log/db.log", // you must make log folder 34 | "datepattern" : ".yyyy-MM-dd" 35 | }, 36 | "errLog" : { 37 | "level" : "info", 38 | "filename" : "./log/err.log", // you must make log floder 39 | "datepattern" : ".yyyy-MM-dd" 40 | }, 41 | "debugLog" : { 42 | "level" : "debug", 43 | "filename" : "./log/debug.log", // you must make log floder 44 | "datepattern" : ".yyyy-MM-dd" 45 | }, 46 | "batchLog" : { 47 | "level" : "info", 48 | "filename" : "./log/batch.log", // you must make log folder 49 | "datepattern" : ".yyyy-MM-dd" 50 | }, 51 | "batchErrLog" : { 52 | "level" : "info", 53 | "filename" : "./log/batch_err.log", // you must make log folder 54 | "datepattern" : ".yyyy-MM-dd" 55 | }, 56 | "cors" : { 57 | "whitelist" : ["::ffff:127.0.0.1", "http://localhost:3000", "http://localhost:3001", "http://localhost:4000", "http://localhost:14608"] 58 | }, 59 | "userAgent" : "eos|api|developer" 60 | } -------------------------------------------------------------------------------- /backend/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "timezone" : "+00:00", 3 | "mongo" : { 4 | "host" : "localhost:27017", 5 | "dbName" : "devwps", 6 | "account" : "devwps", 7 | "passwd" : "devwps123!" 8 | }, 9 | "redis" : { 10 | "hosts" : "localhost", //["localhost", "localhost:16379", "localhost:26379"], 11 | "port" : 6379, 12 | "expire" : { 13 | "default" : 86400 14 | } 15 | }, 16 | "eosNode" : { 17 | "host" :"eosnet.eoseoul.io", 18 | "protocol": "http", 19 | "httpPort" : 8888, 20 | "systemAccount" : "eosio", 21 | "wpsAccount" : "eosio.wps", 22 | "tokenAccount" : "eosio.token", 23 | "symbol" : "EOS", 24 | "chainId" : "a4fe43c897279a677025624555f3835b949f995f87923c97f0392dcff835bfd0", 25 | }, 26 | "webLog" : { 27 | "level" : "info", 28 | "filename" : "./log/web.log", // you must make log folder 29 | "datepattern" : ".yyyy-MM-dd" 30 | }, 31 | "dbLog" : { 32 | "level" : "info", 33 | "filename" : "./log/db.log", // you must make log folder 34 | "datepattern" : ".yyyy-MM-dd" 35 | }, 36 | "errLog" : { 37 | "level" : "info", 38 | "filename" : "./log/err.log", // you must make log floder 39 | "datepattern" : ".yyyy-MM-dd" 40 | }, 41 | "debugLog" : { 42 | "level" : "debug", 43 | "filename" : "./log/debug.log", // you must make log floder 44 | "datepattern" : ".yyyy-MM-dd" 45 | }, 46 | "batchLog" : { 47 | "level" : "info", 48 | "filename" : "./log/batch.log", // you must make log folder 49 | "datepattern" : ".yyyy-MM-dd" 50 | }, 51 | "batchErrLog" : { 52 | "level" : "info", 53 | "filename" : "./log/batch_err.log", // you must make log folder 54 | "datepattern" : ".yyyy-MM-dd" 55 | }, 56 | "cors" : { 57 | "whitelist" : "*" // ["*"] 58 | }, 59 | "userAgent" : "eos|api|development" 60 | } -------------------------------------------------------------------------------- /backend/deploy/eco_developer.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps" : [{ 3 | "name" : "WPS-DEV-API", 4 | "script" : "bin/www", 5 | "instances" : 1, 6 | "exec_mode" : "cluster_mode", 7 | "max_memory_restart" : "800M", 8 | "env": { 9 | "COMMON_VARIABLE": "true", 10 | "PORT" : 3002, 11 | "NODE_ENV": "developer", 12 | "NODE_PATH" : "." 13 | } 14 | }, { 15 | "name" : "WPS-DEV-BATCH-1001", 16 | "script" : "bin/www", 17 | "instances" : 1, 18 | "exec_mode" : "cluster_mode", 19 | "env": { 20 | "COMMON_VARIABLE": "true", 21 | "PORT" : 3003, 22 | "NODE_PATH": ".", 23 | "NODE_ENV": "developer", 24 | "NODE_JOB_GROUP" : "libBlock", 25 | "NODE_BATCH_ID" : 1001 26 | }, 27 | "env_development" : { 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /backend/deploy/eco_development.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps" : [{ 3 | "name" : "WPS-DEV-API", 4 | "script" : "bin/www", 5 | "instances" : 1, 6 | "exec_mode" : "cluster_mode", 7 | "max_memory_restart" : "800M", 8 | "env": { 9 | "COMMON_VARIABLE": "true", 10 | "PORT" : 3000, 11 | "NODE_ENV": "development", 12 | "NODE_PATH" : "." 13 | } 14 | }, { 15 | "name" : "WPS-DEV-BATCH-1001", 16 | "script" : "bin/www", 17 | "instances" : 1, 18 | "exec_mode" : "cluster_mode", 19 | "env": { 20 | "COMMON_VARIABLE": "true", 21 | "PORT" : 3001, 22 | "NODE_PATH": ".", 23 | "NODE_ENV": "development", 24 | "NODE_JOB_GROUP" : "libBlock", 25 | "NODE_BATCH_ID" : 1001 26 | }, 27 | "env_development" : { 28 | } 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /backend/doc/mongo.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | // initialize lib db 4 | 5 | db.libactions.drop(); 6 | db.libactionstakes.drop(); 7 | db.libactionunstakes.drop(); 8 | db.libcommittees.drop(); 9 | db.libproposals.drop(); 10 | db.libproposers.drop(); 11 | db.libreviewers.drop(); 12 | db.libsummaries.drop(); 13 | db.libupdateproposals.drop(); 14 | db.libupdatevotings.drop(); 15 | db.libvoterinfos.drop(); 16 | db.libvotinginfos.drop(); 17 | 18 | 19 | db.wpsinfos.drop(); -------------------------------------------------------------------------------- /backend/external_apis/eos_api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | Eos = require('eosjs'), 5 | config = require('config'); 6 | 7 | const {format /*, api, ecc, json, Fcbuffer*/} = Eos.modules; 8 | 9 | const eosNodeConfig = config.eosNode; 10 | 11 | function getConfig(key, httpEndPoint) { 12 | const endpoint = getEndpoint(httpEndPoint); 13 | return { 14 | keyProvider: key, // || eosNodeConfig.watchmanPriKey, 15 | httpEndpoint: endpoint, 16 | // mockTransactions: () => 'pass', // or 'fail' 17 | // transactionHeaders: (expireInSeconds, callback) => { callback(null/*error*/, headers)}, 18 | expireInSeconds: 60, 19 | broadcast: true, 20 | debug: false, 21 | sign: true, 22 | chainId: eosNodeConfig.chainId, 23 | verbose: false, 24 | logger: { 25 | log: false ? console.log : null, // null to disable 26 | error: false ? console.error : null, 27 | } 28 | }; 29 | } 30 | 31 | function getEndpoint(endpoint) { 32 | if (_.isEmpty(endpoint)) { 33 | endpoint = `${eosNodeConfig.protocol}://${eosNodeConfig.host}:${eosNodeConfig.httpPort}`; 34 | } 35 | return endpoint; 36 | } 37 | 38 | function getInfo(endpoint) { 39 | const options = getConfig(endpoint); 40 | const eos = Eos(options); 41 | return eos.getInfo({}) 42 | .catch(function(err) { 43 | if (typeof err === 'string') { 44 | throw JSON.parse(err); 45 | } 46 | throw err; 47 | }); 48 | } 49 | 50 | function getBlock(blockId, endpoint) { 51 | const options = getConfig(endpoint); 52 | const eos = Eos(options); 53 | return eos.getBlock(blockId) 54 | .catch(function(err) { 55 | if (typeof err === 'string') { 56 | throw JSON.parse(err); 57 | } 58 | throw err; 59 | }); 60 | } 61 | 62 | function getTableRows(code, scope, table, tableKey, lowerBound, limit, keyType, indexPosition, endpoint) { 63 | const options = getConfig(endpoint); 64 | const eos = Eos(options); 65 | 66 | const params = { 67 | json : true, // {type : "bool", "default": false}, 68 | code : code, 69 | scope : scope, 70 | table : table, 71 | table_key : tableKey, 72 | lower_bound : lowerBound, 73 | //upper_bound: {type : "string", default: '-1'}, 74 | limit : limit, 75 | key_type : keyType, 76 | index_position : indexPosition 77 | }; 78 | 79 | return eos.getTableRows(params) 80 | .catch(function(err) { 81 | if (typeof err === 'string') { 82 | throw JSON.parse(err); 83 | } 84 | throw err; 85 | }); 86 | } 87 | 88 | function getVoterInfo(voter) { 89 | // console.log(format.encodeName(voter, false)); 90 | const systemAccount = eosNodeConfig.systemAccount; 91 | return getTableRows(systemAccount, systemAccount, 'voters', null, format.encodeName(voter, false), 1, 'i64', 1) 92 | .then(function(result) { 93 | if (!_.isEmpty(result.rows)) { 94 | return result.rows[0]; 95 | } 96 | return {}; 97 | }); 98 | } 99 | 100 | function getProposalById(proposalId) { 101 | const wpsAccount = eosNodeConfig.wpsAccount; 102 | return getTableRows(wpsAccount, wpsAccount, 'proposals', null, proposalId, 1, 'i64', 2) 103 | .then(function(result) { 104 | if (!_.isEmpty(result.rows)) { 105 | return result.rows[0]; 106 | } 107 | return {}; 108 | }); 109 | } 110 | 111 | function getProposalByOwner(proposer) { 112 | const wpsAccount = eosNodeConfig.wpsAccount; 113 | return getTableRows(wpsAccount, wpsAccount, 'proposals', null, format.encodeName(proposer, false), 1, 'i64', 1) 114 | .then(function(result) { 115 | if (!_.isEmpty(result.rows)) { 116 | return result.rows[0]; 117 | } 118 | return {}; 119 | }); 120 | } 121 | 122 | function getRejectedProposalById(proposalId) { 123 | const wpsAccount = eosNodeConfig.wpsAccount; 124 | return getTableRows(wpsAccount, wpsAccount, 'rejectedpros', null, proposalId, 1, 'i64', 2) 125 | .then(function(result) { 126 | if (!_.isEmpty(result.rows)) { 127 | return result.rows[0]; 128 | } 129 | return {}; 130 | }); 131 | } 132 | 133 | function getFinishedProposalById(proposalId) { 134 | const wpsAccount = eosNodeConfig.wpsAccount; 135 | return getTableRows(wpsAccount, wpsAccount, 'finishedpros', null, proposalId, 1, 'i64', 2) 136 | .then(function(result) { 137 | if (!_.isEmpty(result.rows)) { 138 | return result.rows[0]; 139 | } 140 | return {}; 141 | }); 142 | } 143 | 144 | function getCurrencyStats(endpoint) { 145 | const tokenAccount = eosNodeConfig.tokenAccount; 146 | const options = getConfig(endpoint); 147 | const eos = Eos(options); 148 | return eos.getCurrencyStats(tokenAccount, 'EOS'); 149 | } 150 | 151 | function commitVote(params, key, authorization, endpoint) { // eosio.wps contract 152 | const options = getConfig(key, endpoint); 153 | const eos = Eos(options); 154 | const wpsAccount = eosNodeConfig.wpsAccount; 155 | return eos.contract(wpsAccount) 156 | .then((contract) => { 157 | return contract.commitvote(params, {authorization: authorization}); 158 | }) 159 | .catch(function(err) { 160 | if (typeof err === 'string') { 161 | throw JSON.parse(err); 162 | } 163 | throw err; 164 | }); 165 | } 166 | 167 | function rollbackVote(params, key, authorization, endpoint) { // eosio.wps contract 168 | const options = getConfig(key, endpoint); 169 | const eos = Eos(options); 170 | const wpsAccount = eosNodeConfig.wpsAccount; 171 | return eos.contract(wpsAccount) 172 | .then((contract) => { 173 | return contract.rollbackvote(params, {authorization: authorization}); 174 | }) 175 | .catch(function(err) { 176 | if (typeof err === 'string') { 177 | throw JSON.parse(err); 178 | } 179 | throw err; 180 | }); 181 | } 182 | 183 | function checkExpire(params, key, authorization, endpoint) { // eosio.wps contract 184 | const options = getConfig(key, endpoint); 185 | const eos = Eos(options); 186 | const wpsAccount = eosNodeConfig.wpsAccount; 187 | return eos.contract(wpsAccount) 188 | .then((contract) => { 189 | return contract.checkexpire(params, {authorization: authorization}); 190 | }) 191 | .catch(function(err) { 192 | if (typeof err === 'string') { 193 | throw JSON.parse(err); 194 | } 195 | throw err; 196 | }); 197 | } 198 | 199 | function getEos(endpoint) { 200 | const options = getConfig(endpoint); 201 | return Eos(options); 202 | } 203 | 204 | module.exports = exports = {getInfo, getBlock, getTableRows, 205 | getVoterInfo, getProposalById, getProposalByOwner, 206 | getRejectedProposalById, getFinishedProposalById, 207 | getCurrencyStats, 208 | commitVote, rollbackVote, checkExpire, 209 | getEos, 210 | }; 211 | 212 | 213 | -------------------------------------------------------------------------------- /backend/libs/api/committee.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongo = require('models/mongo'); 4 | 5 | const Committee = mongo.LibCommittee; 6 | 7 | function getCommittees() { 8 | return Committee.find().sort({_id : 1}); 9 | } 10 | 11 | module.exports = exports = {getCommittees}; 12 | -------------------------------------------------------------------------------- /backend/libs/api/proposal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongo = require('models/mongo'); 4 | 5 | const Proposal = mongo.LibProposal; 6 | 7 | function getProposals(params) { 8 | const committee = params.committee; 9 | const order = params.order; 10 | if (order === 'hot') { 11 | return Proposal.find({committee : committee}).sort({total_votes : -1}); 12 | } else { 13 | return Proposal.find({committee : committee}).sort({_id : -1}); 14 | } 15 | } 16 | 17 | function getProposal(proposalId) { 18 | return Proposal.findOne({proposal_id : proposalId}); 19 | } 20 | 21 | module.exports = exports = {getProposals, getProposal}; 22 | -------------------------------------------------------------------------------- /backend/libs/api/proposer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongo = require('models/mongo'); 4 | 5 | const Proposal = mongo.LibProposal; 6 | 7 | function getProposals(proposer) { 8 | return Proposal.find({proposer : proposer}).sort({_id : -1}); 9 | } 10 | 11 | module.exports = exports = {getProposals}; 12 | -------------------------------------------------------------------------------- /backend/libs/api/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongo = require('models/mongo'); 4 | 5 | const Summary = mongo.LibSummary; 6 | 7 | function getSummary() { 8 | return Summary.findOne(); 9 | } 10 | 11 | module.exports = exports = {getSummary}; 12 | -------------------------------------------------------------------------------- /backend/libs/api/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SUtil = require('libs/util'); 6 | 7 | const User = mongo.User; 8 | 9 | async function createUser(params) { 10 | return User.create(params); 11 | } 12 | 13 | async function updateUser(params) { 14 | return User.findOneAndUpdate({eos_account : params.eos_account}, {$set : params}); 15 | } 16 | 17 | async function getUser(eosAccount) { 18 | return User.findOne({eos_account : eosAccount}); 19 | } 20 | 21 | async function deleteUser(eosAccount) { 22 | if (_.isEmpty(eosAccount)) { 23 | return SUtil.createErrObject('BAD_REQUEST', {reason : 'eos_account is empty'}); 24 | } else if (process.env.NODE_ENV !== 'developer') { 25 | throw SUtil.createErrObject('BAD_REQUEST', {reason : 'not support'}); 26 | } 27 | return User.deleteOne({eos_account : eosAccount}); 28 | } 29 | 30 | module.exports = exports = {createUser, updateUser, getUser, deleteUser}; 31 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/committee/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SEnum = require('libs/enum'); 6 | // log = require('libs/log'); 7 | 8 | const Committee = mongo.LibCommittee; 9 | const Proposal = mongo.LibProposal; 10 | 11 | async function regcommittee(action, trx) { 12 | let data = action.data; 13 | if (_.isEmpty(data)) { 14 | return; 15 | } 16 | return Committee.create(data); 17 | } 18 | 19 | async function edcommittee(action, trx) { 20 | let data = action.data; 21 | if (_.isEmpty(data)) { 22 | return; 23 | } 24 | return Committee.updateOne({committeeman: data.committeeman}, {$set : data}); 25 | } 26 | 27 | async function rmvcommittee(action, trx) { 28 | let data = action.data; 29 | if (_.isEmpty(data)) { 30 | return; 31 | } 32 | return Committee.deleteOne({committeeman: data.committeeman}); 33 | } 34 | 35 | async function rejectfund(action, trx) { 36 | let data = action.data; 37 | if (_.isEmpty(data)) { 38 | return; 39 | } 40 | await Proposal.updateOne({proposal_id : data.proposal_id}, {$set : {status : SEnum.PROPOSAL_STATUS_REJECT}}); 41 | } 42 | 43 | module.exports = exports = {regcommittee, edcommittee, rmvcommittee, rejectfund}; 44 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/delegate/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongo = require('models/mongo'), 4 | SEnum = require('libs/enum'); 5 | 6 | const ActionStake = mongo.LibActionStake; 7 | const ActionUnstake = mongo.LibActionUnstake; 8 | const UpdateVoting = mongo.LibUpdateVoting; 9 | 10 | async function delegatebw(action, trx) { 11 | let data = Object.assign({}, action.data, {trx_id : trx.id}); 12 | await ActionStake.create(data); 13 | await UpdateVoting.create({account : data.receiver, action : SEnum.VOTE_ACTION_STAKE}); 14 | } 15 | 16 | async function undelegatebw(action, trx) { 17 | let data = Object.assign({}, action.data, {trx_id : trx.id}); 18 | await ActionUnstake.create(data); 19 | await UpdateVoting.create({account : data.receiver, action : SEnum.VOTE_ACTION_UNSTAKE}); 20 | } 21 | 22 | module.exports = exports = {delegatebw, undelegatebw}; 23 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Promise = require('bluebird'), 4 | _ = require('lodash'), 5 | config = require('config'), 6 | eosApi = require('external_apis/eos_api'), 7 | mongo = require('models/mongo'), 8 | log = require('libs/log'), 9 | committee = require('./committee'), 10 | reviewer = require('./reviewer'), 11 | proposer = require('./proposer'), 12 | proposal = require('./proposal'), 13 | watchman = require('./watchman'), 14 | delegate = require('./delegate'), 15 | vote = require('./vote'); 16 | 17 | const batchErrLog = log.batchErrLog; 18 | const batchLog = log.batchLog; 19 | const Summary = mongo.LibSummary; 20 | const Action = mongo.LibAction; 21 | 22 | const eosNodeConfig = config.eosNode; 23 | 24 | function getAccountsFuncMap() { 25 | return [{ 26 | account : eosNodeConfig.systemAccount, //'eosio', 27 | funcMap : delegate 28 | }, { 29 | account : eosNodeConfig.wpsAccount, //'eosio.wps', 30 | funcMap : _.extend(committee, reviewer, proposer, proposal, vote, watchman) 31 | }]; 32 | } 33 | 34 | async function processActions() { 35 | let blockNum = 0; 36 | let blockId = ''; 37 | let summary = await Summary.findOne(); 38 | if (_.isEmpty(summary)) { 39 | batchErrLog.info({reason : 'NOT_EXIST_SUMMARY'}); 40 | return; 41 | } 42 | blockNum = summary.block_num; 43 | 44 | const chainInfo = await eosApi.getInfo(); 45 | if (_.isEmpty(chainInfo)) { 46 | batchErrLog.info({reason : 'CANNOT_GET_CHAIN_INFO'}); 47 | return; 48 | } 49 | let diff = chainInfo.last_irreversible_block_num - blockNum; 50 | batchLog.info(`blockNum : ${blockNum}`); 51 | batchLog.info(`diff : ${diff}`); 52 | if (diff <= 0) { 53 | return; 54 | } 55 | if (diff > 100) { 56 | diff = 100; 57 | } 58 | return Promise.each(_.range(blockNum + 1, blockNum + diff + 1), (_blockNum) => { 59 | return eosApi.getBlock(_blockNum) 60 | .catch((err) => { 61 | if (!_.isEmpty(err.message)) { 62 | const message = JSON.parse(err.message); 63 | const error = message.error || {}; 64 | if (error.code === 3100002) { // unknown_block_exception 65 | err.reason = 'NOT_EXIST_BLOCK'; 66 | err.blockNum = _blockNum; 67 | batchErrLog.info(err); 68 | return {}; 69 | } 70 | } 71 | err.blockNum = _blockNum; 72 | throw err; 73 | }) 74 | .then(async function(_block) { 75 | if (_.isEmpty(_block)) { 76 | return; 77 | } 78 | const transactions = _block.transactions; 79 | blockId = _block.id; 80 | if (!_.isEmpty(transactions)) { 81 | return Promise.each(transactions, async function(transaction) { 82 | const trx = transaction.trx; 83 | return _processActions(transaction.trx.transaction.actions, trx, blockNum); 84 | }); 85 | } 86 | }) 87 | .then(async function() { 88 | if (_.isEmpty(summary)) { 89 | summary = await Summary.create({block_id : blockId, block_num : _blockNum}); 90 | } 91 | return Summary.updateOne({_id : summary._id}, {block_num : _blockNum, block_id : blockId}); 92 | }) 93 | .catch((err) => { 94 | batchErrLog.info(err); 95 | }); 96 | }); 97 | } 98 | 99 | async function _processActions(actions, trx, blockNum) { 100 | let accountsFuncMap = getAccountsFuncMap(); 101 | return Promise.each(actions, async function(action) { 102 | let account = _.find(accountsFuncMap, {account : action.account}); 103 | if (!_.isEmpty(account) && !_.isNil(account.funcMap[action.name])) { 104 | await _saveAction(action, trx, blockNum); 105 | await account.funcMap[action.name](action, trx); 106 | } 107 | }); 108 | } 109 | 110 | async function _saveAction(action, trx, blockNum) { 111 | const actionObj = { 112 | trx_id : trx.trx_id, 113 | block_num : blockNum, 114 | account : action.account, 115 | name : action.name, 116 | authorization : _.map(action.authorization, (authorization) => `${authorization.actor}@${authorization.permission}`), 117 | raw : action 118 | }; 119 | await Action.create(actionObj); 120 | } 121 | 122 | module.exports = exports = {processActions}; 123 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/proposal/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SEnum = require('libs/enum'); 6 | 7 | const Proposal = mongo.LibProposal; 8 | const UpdateProposal = mongo.LibUpdateProposal; 9 | 10 | async function regproposal(action) { 11 | const data = Object.assign({}, action.data, {status : SEnum.PROPOSAL_STATUS_PENDING}); 12 | if (_.isEmpty(data)) { 13 | return; 14 | } 15 | await Proposal.create(data); 16 | await UpdateProposal.create({proposer : data.proposer, proposal_id : 0}); 17 | } 18 | 19 | async function editproposal(action) { 20 | const data = action.data; 21 | if (_.isEmpty(data)) { 22 | return; 23 | } 24 | await Proposal.updateOne({proposer : data.proposer}, {$set : data}); 25 | await UpdateProposal.create({proposer : data.proposer, proposal_id : 0}); 26 | } 27 | 28 | function rmvproposal(action) { 29 | const data = action.data; 30 | if (_.isEmpty(data)) { 31 | return; 32 | } 33 | return Proposal.deleteOne({proposer : data.proposer, status : {$nin : [SEnum.PROPOSAL_STATUS_REJECT, SEnum.PROPOSAL_STATUS_COMPLETE]}}); 34 | } 35 | 36 | module.exports = exports = {regproposal, editproposal, rmvproposal}; 37 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/proposer/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'); 5 | 6 | const Proposer = mongo.LibProposer; 7 | const UpdateProposal = mongo.LibUpdateProposal; 8 | 9 | async function regproposer(action, trx) { 10 | const data = action.data; 11 | if (_.isEmpty(data)) { 12 | return; 13 | } 14 | return Proposer.create(data); 15 | } 16 | 17 | async function editproposer(action, trx) { 18 | const data = action.data; 19 | if (_.isEmpty(data)) { 20 | return; 21 | } 22 | return Proposer.updateOne({account : data.account}, {$set : data}); 23 | } 24 | 25 | async function rmvproposer(action, trx) { 26 | let data = action.data; 27 | if (_.isEmpty(data)) { 28 | return; 29 | } 30 | return Proposer.deleteOne({account: data.account}); 31 | } 32 | 33 | async function claimfunds(action, trx) { 34 | const data = action.data; 35 | return UpdateProposal.create({proposer : data.account, proposal_id : data.proposal_id}); 36 | } 37 | 38 | module.exports = exports = {regproposer, editproposer, rmvproposer, claimfunds}; 39 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/reviewer/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SEnum = require('libs/enum'); 6 | 7 | const Reviewer = mongo.LibReviewer; 8 | const UpdateProposal = mongo.LibUpdateProposal; 9 | const UpdateVoting = mongo.LibUpdateVoting; 10 | 11 | async function regreviewer(action) { 12 | let data = Object.assign({}, action.data, {account : action.data.reviewer}); 13 | return Reviewer.create(data); 14 | } 15 | 16 | async function editreviewer(action) { 17 | let data = Object.assign({}, action.data, {account : action.data.reviewer}); 18 | return Reviewer.updateOne({account : data.account}, {$set : data}); 19 | } 20 | 21 | async function rmvreviewer(action) { 22 | const account = action.data.reviewer; 23 | if (_.isEmpty(account)) { 24 | return; 25 | } 26 | return Reviewer.deleteOne({account : account}); 27 | } 28 | 29 | async function acceptprop(action) { 30 | const data = action.data; 31 | return UpdateProposal.create({proposal_id : data.proposal_id}); 32 | } 33 | 34 | async function rejectprop(action) { 35 | const data = action.data; 36 | return UpdateProposal.create({proposal_id : data.proposal_id}); 37 | } 38 | 39 | async function checkvote(action) { 40 | const data = action.data; 41 | return UpdateVoting.create({proposal_id : data.proposal_id, action : SEnum.VOTE_ACTION_CHECK}); 42 | } 43 | 44 | async function approve(action) { 45 | const data = action.data; 46 | return UpdateProposal.create({proposal_id : data.proposal_id}); 47 | } 48 | 49 | async function rmvreject(action) { 50 | const data = action.data; 51 | return UpdateProposal.create({proposal_id : data.proposal_id}); 52 | } 53 | 54 | async function rmvcompleted(action) { 55 | } 56 | 57 | module.exports = exports = {regreviewer, editreviewer, rmvreviewer, acceptprop, rejectprop, checkvote, approve, rmvreject, rmvcompleted}; 58 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/vote/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SEnum = require('libs/enum'); 6 | 7 | const UpdateVoting = mongo.LibUpdateVoting; 8 | 9 | async function vote(action, trx) { 10 | const data = action.data; 11 | if (_.isEmpty(data)) { 12 | return; 13 | } 14 | return UpdateVoting.create({account : data.voter, proposal_id : data.proposal_id, action : SEnum.VOTE_ACTION_VOTE, is_agree : data.is_agree}); 15 | } 16 | 17 | async function unvote(action, trx) { 18 | const data = action.data; 19 | if (_.isEmpty(data)) { 20 | return; 21 | } 22 | return UpdateVoting.create({account : data.voter, proposal_id : data.proposal_id, action : SEnum.VOTE_ACTION_UNVOTE}); 23 | } 24 | 25 | module.exports = exports = {vote, unvote}; 26 | -------------------------------------------------------------------------------- /backend/libs/batch/process_actions/watchman/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'); 5 | 6 | const UpdateProposal = mongo.LibUpdateProposal; 7 | 8 | async function commitvote(action) { 9 | const data = action.data; 10 | if (_.isEmpty(data)) { 11 | return; 12 | } 13 | return UpdateProposal.create({proposal_id : data.proposal_id}); 14 | } 15 | 16 | async function rollbackvote(action) { 17 | const data = action.data; 18 | if (_.isEmpty(data)) { 19 | return; 20 | } 21 | return UpdateProposal.create({proposal_id : data.proposal_id}); 22 | } 23 | 24 | async function checkexpire(action) { 25 | const data = action.data; 26 | if (_.isEmpty(data)) { 27 | return; 28 | } 29 | return UpdateProposal.create({proposal_id : data.proposal_id}); 30 | } 31 | 32 | module.exports = exports = {commitvote, rollbackvote, checkexpire}; 33 | -------------------------------------------------------------------------------- /backend/libs/batch/update_proposals/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Promise = require('bluebird'), 4 | _ = require('lodash'), 5 | eosApi = require('external_apis/eos_api'), 6 | mongo = require('models/mongo'), 7 | SEnum = require('libs/enum'), 8 | log = require('libs/log'); 9 | 10 | const batchErrLog = log.batchErrLog; 11 | const batchLog = log.batchLog; 12 | 13 | const UpdateProposal = mongo.LibUpdateProposal; 14 | const Proposal = mongo.LibProposal; 15 | 16 | async function updateProposals() { 17 | // batchLog.info('updateProposals'); 18 | const chainInfo = await eosApi.getInfo(); 19 | if (_.isEmpty(chainInfo)) { 20 | batchErrLog.info({reason : 'CANNOT_GET_CHAIN_INFO'}); 21 | return; 22 | } 23 | const updateProps = await UpdateProposal.find().limit(30).sort({_id : 1}); 24 | if (!_.isEmpty(updateProps)) { 25 | return Promise.each(updateProps, function(prop) { 26 | return _updateProposal(prop) 27 | .catch((err) => { 28 | err.updateProp = prop; 29 | batchErrLog.info = err; 30 | throw err; 31 | }); 32 | }); 33 | } 34 | } 35 | 36 | async function _updateProposal(prop) { 37 | let proposal = null; 38 | if (prop.proposal_id > 0) { 39 | const proposalId = prop.proposal_id; 40 | proposal = await Promise.join( 41 | eosApi.getProposalById(proposalId), 42 | eosApi.getRejectedProposalById(proposalId), 43 | eosApi.getFinishedProposalById(proposalId), 44 | (proposal, rejectedProposal, finishedProposal) => { 45 | if (!_.isEmpty(proposal)) { 46 | return proposal; 47 | } else if (!_.isEmpty(rejectedProposal)) { 48 | return rejectedProposal; 49 | } else if (!_.isEmpty(finishedProposal)) { 50 | return finishedProposal; 51 | } 52 | return {}; 53 | }); 54 | if (!_.isEmpty(proposal)) { 55 | delete proposal.total_votes; 56 | delete proposal.agree_votes; 57 | delete proposal.disagree_votes; 58 | await Proposal.updateOne({proposal_id : proposal.id}, {$set : proposal}); 59 | } 60 | } else { 61 | proposal = await eosApi.getProposalByOwner(prop.proposer); 62 | if (!_.isEmpty(proposal)) { 63 | delete proposal.total_votes; 64 | delete proposal.agree_votes; 65 | delete proposal.disagree_votes; 66 | 67 | proposal.proposal_id = proposal.id; 68 | await Proposal.updateOne({proposer : prop.proposer, status : SEnum.PROPOSAL_STATUS_PENDING}, {$set :proposal}); 69 | } 70 | } 71 | if (!_.isEmpty(prop)) { 72 | return prop.remove(); 73 | } 74 | } 75 | 76 | module.exports = exports = {updateProposals}; 77 | -------------------------------------------------------------------------------- /backend/libs/batch/update_summary/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | SEnum = require('libs/enum'), 6 | log = require('libs/log'); 7 | 8 | // const errLog = log.errLog; 9 | const batchLog = log.batchLog; 10 | 11 | const Summary = mongo.LibSummary; 12 | const Proposal = mongo.LibProposal; 13 | const VotingInfo = mongo.LibVotingInfo; 14 | 15 | /* 16 | PROPOSAL_STATUS_PENDING : 1, 17 | PROPOSAL_STATUS_REJECTED : 2, 18 | PROPOSAL_STATUS_ON_VOTE : 3, 19 | PROPOSAL_STATUS_FINISHED_VOTING : 4, 20 | PROPOSAL_STATUS_CHECK_VOTE : 5, // check count of votes 21 | PROPOSAL_STATUS_CHECKED_VOTE : 6, // checked count of votes by platform 22 | PROPOSAL_STATUS_APPROVED : 7, // approve 23 | PROPOSAL_STATUS_COMPLETED : 8, 24 | */ 25 | 26 | async function updateSummary() { 27 | // batchLog.info('updateSummary'); 28 | const summary = await Summary.findOne(); 29 | if (_.isEmpty(summary)) { 30 | return; 31 | } 32 | const statusCnts = await Proposal.aggregate([{ 33 | $group : { 34 | _id : '$status', 35 | count : {$sum : 1} 36 | } 37 | }]); 38 | 39 | const summaryObj = { 40 | total_proposals : 0, 41 | funded_proposals : 0, 42 | ongoing_proposals : 0, 43 | total_voters : 0 44 | }; 45 | 46 | _.forEach(statusCnts, function(status) { 47 | if (status._id !== SEnum.PROPOSAL_STATUS_REJECTED) { 48 | summaryObj.total_proposals += status.count; 49 | } 50 | if (status._id === SEnum.PROPOSAL_STATUS_COMPLETED) { 51 | summaryObj.funded_proposals += status.count; 52 | } 53 | if (status._id === SEnum.PROPOSAL_STATUS_PENDING || status._id === SEnum.PROPOSAL_STATUS_ON_VOTE || status._id === SEnum.PROPOSAL_STATUS_FINISHED_VOTING || 54 | status._id === SEnum.PROPOSAL_STATUS_CHECK_VOTE || status._id === SEnum.PROPOSAL_STATUS_CHECKED_VOTE || 55 | status._id === SEnum.PROPOSAL_STATUS_APPROVED) { 56 | summaryObj.ongoing_proposals += status.count; 57 | } 58 | }); 59 | summaryObj.total_voters = await VotingInfo.distinct('account').countDocuments(); 60 | return Summary.updateOne({_id : summary._id}, {$set : summaryObj}); 61 | } 62 | 63 | module.exports = exports = {updateSummary}; 64 | -------------------------------------------------------------------------------- /backend/libs/batch/update_votings/check_voting.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'), 5 | eosApi = require('external_apis/eos_api'), 6 | SUtil = require('libs/util'), 7 | SEnum = require('libs/enum'), 8 | log = require('libs/log'); 9 | 10 | const batchErrLog = log.batchErrLog; 11 | 12 | const Proposal = mongo.LibProposal; 13 | const WpsInfo = mongo.WpsInfo; 14 | 15 | async function checkVoting(updateVoting) { 16 | let wpsInfo = await WpsInfo.findOne(); 17 | if (_.isEmpty(wpsInfo)) { 18 | batchErrLog.info({reason : 'CANNT_GET_WPS_INFO_FROM_DB'}); 19 | return; 20 | } 21 | 22 | const totalPercent = wpsInfo.total_voting_percent; 23 | const agreePercent = wpsInfo.agree_percent; 24 | const watchmanAccount = wpsInfo.watchman_account; 25 | const key = wpsInfo.watchman_prv; 26 | 27 | let proposal = await Proposal.findOne({proposal_id : updateVoting.proposal_id}); 28 | if (_.isEmpty(proposal)) { 29 | batchErrLog.info({reason : 'CANNT_GET_PROPOSAL_FROM_DB', updateVoting}); 30 | return updateVoting.remove(); 31 | } 32 | let stats = await eosApi.getCurrencyStats(); 33 | let proposalFromNode = await eosApi.getProposalById(updateVoting.proposal_id); 34 | if (_.isEmpty(stats) || _.isEmpty(proposalFromNode)) { 35 | batchErrLog.info({reason : 'CANNT_GET_STATS_OR_PROPOSAL_FROM_NODE', updateVoting, stats, proposalFromNode}); 36 | return updateVoting.remove(); 37 | } 38 | if (proposalFromNode.status !== SEnum.PROPOSAL_STATUS_CHECK_VOTE) { 39 | batchErrLog.info({reason : 'DIFFERENT_PROPOSAL_STATUS', proposal, proposalFromNode}); 40 | return updateVoting.remove(); 41 | } 42 | let supplyAmount = SUtil.toAmount(stats.EOS.supply); 43 | const params = { 44 | watchman : watchmanAccount, 45 | proposal_id : proposal.proposal_id, 46 | total_votes : proposal.total_votes, 47 | agree_votes : proposal.agree_votes, 48 | disagree_votes : proposal.disagree_votes, 49 | }; 50 | if (_checkVoting(supplyAmount, proposal, totalPercent, agreePercent) === true) { 51 | await eosApi.commitVote(params, key, watchmanAccount); 52 | } else { 53 | await eosApi.rollbackVote(params, key, watchmanAccount); 54 | } 55 | await updateVoting.remove(); 56 | } 57 | 58 | function _checkVoting(supplyAmount, proposal, totalPercent, agreePercent) { 59 | const totalVotesRate = proposal.total_votes / supplyAmount * 100; 60 | if (totalVotesRate >= totalPercent) { 61 | if (proposal.disagree_votes === 0) { 62 | return true; 63 | } 64 | const agreeRate = (proposal.agree_votes - proposal.disagree_votes) / proposal.disagree_votes * 100; 65 | if (agreeRate >= agreePercent) { 66 | return true; 67 | } else { 68 | batchErrLog.info({reason : 'ROLLBACK_VOTES', proposal, supplyAmount}); 69 | return false; 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | module.exports = exports = checkVoting; 76 | 77 | -------------------------------------------------------------------------------- /backend/libs/batch/update_votings/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Promise = require('bluebird'), 4 | _ = require('lodash'), 5 | eosApi = require('external_apis/eos_api'), 6 | mongo = require('models/mongo'), 7 | SEnum = require('libs/enum'), 8 | log = require('libs/log'), 9 | checkVoting = require('./check_voting'), 10 | vote = require('./vote'), 11 | unvote = require('./unvote'), 12 | stakeunstake = require('./stakeunstake'); 13 | 14 | const batchErrLog = log.batchErrLog; 15 | const batchLog = log.batchLog; 16 | 17 | const UpdateVoting = mongo.LibUpdateVoting; 18 | const Proposal = mongo.LibProposal; 19 | const VotingInfo = mongo.LibVotingInfo; 20 | const VoterInfo = mongo.LibVoterInfo; 21 | 22 | async function updateVotings() { 23 | // batchLog.info('updateVotings'); 24 | const chainInfo = await eosApi.getInfo(); 25 | if (_.isEmpty(chainInfo)) { 26 | batchErrLog.info({reason : 'CANNOT_GET_CHAIN_INFO'}); 27 | return; 28 | } 29 | 30 | let updateVotings = await UpdateVoting.find().limit(30).sort({_id : 1}); 31 | if (!_.isEmpty(updateVotings)) { 32 | return Promise.each(updateVotings, function(updateVoting) { 33 | return _updateVoting(updateVoting) 34 | .catch((err) => { 35 | err.updateVoting = updateVoting; 36 | batchErrLog.info(err); 37 | throw err; 38 | }); 39 | }); 40 | } 41 | } 42 | 43 | async function _updateVoting(updateVoting) { 44 | if (updateVoting.action === SEnum.VOTE_ACTION_CHECK) { 45 | return checkVoting(updateVoting); 46 | } 47 | let votingInfo = await VotingInfo.findOne({account : updateVoting.account, proposal_id : updateVoting.proposal_id}); 48 | if (_.isEmpty(votingInfo) && updateVoting.action !== SEnum.VOTE_ACTION_VOTE) { 49 | return updateVoting.remove(); 50 | } 51 | let voter = await eosApi.getVoterInfo(updateVoting.account); 52 | let proposal = await Proposal.findOne({proposal_id : updateVoting.proposal_id}); 53 | if (_.isEmpty(voter) || _.isEmpty(proposal)) { 54 | return updateVoting.remove(); 55 | } 56 | const voterStaked = parseInt(voter.staked, 10); 57 | if (updateVoting.action === SEnum.VOTE_ACTION_VOTE) { 58 | await vote(proposal, updateVoting, votingInfo, voterStaked); 59 | } else if (updateVoting.action === SEnum.VOTE_ACTION_UNVOTE) { 60 | unvote(proposal, votingInfo); 61 | await votingInfo.remove(); 62 | votingInfo = null; 63 | } else if (updateVoting.action === SEnum.VOTE_ACTION_STAKE || updateVoting.action === SEnum.VOTE_ACTION_UNSTAKE) { 64 | stakeunstake(proposal, votingInfo, voterStaked); 65 | } 66 | if (!_.isEmpty(votingInfo)) { 67 | votingInfo.staked = voterStaked; 68 | votingInfo.is_agree = updateVoting.is_agree; 69 | await votingInfo.save(); 70 | } 71 | await proposal.save(); 72 | await updateVoting.remove(); 73 | 74 | await VoterInfo.create({account : updateVoting.account, staked : voterStaked}); 75 | } 76 | 77 | module.exports = exports = {updateVotings}; 78 | -------------------------------------------------------------------------------- /backend/libs/batch/update_votings/stakeunstake.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function stakeunstake(proposal, votingInfo, voterStaked) { 4 | proposal.total_votes -= votingInfo.staked; 5 | proposal.total_votes += voterStaked; 6 | if (votingInfo.is_agree === true) { 7 | proposal.agree_votes -= votingInfo.staked; 8 | proposal.agree_votes += voterStaked; 9 | } else { 10 | proposal.disagree_votes -= votingInfo.staked; 11 | proposal.disagree_votes += voterStaked; 12 | } 13 | } 14 | 15 | module.exports = exports = stakeunstake; 16 | -------------------------------------------------------------------------------- /backend/libs/batch/update_votings/unvote.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function unvote(proposal, votingInfo) { 4 | proposal.total_votes -= votingInfo.staked; 5 | if (votingInfo.is_agree === true) { 6 | proposal.agree_votes -= votingInfo.staked; 7 | } else { 8 | proposal.disagree_votes -= votingInfo.staked; 9 | } 10 | } 11 | 12 | module.exports = exports = unvote; 13 | -------------------------------------------------------------------------------- /backend/libs/batch/update_votings/vote.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | mongo = require('models/mongo'); 5 | 6 | const VotingInfo = mongo.LibVotingInfo; 7 | 8 | async function vote(proposal, updateVoting, votingInfo, voterStaked) { 9 | if (_.isEmpty(votingInfo)) { 10 | await VotingInfo.create({proposal_id : updateVoting.proposal_id, account : updateVoting.account, staked : voterStaked, is_agree : updateVoting.is_agree}); 11 | proposal.total_votes += voterStaked; 12 | if (updateVoting.is_agree === true) { 13 | proposal.agree_votes += voterStaked; 14 | } else { 15 | proposal.disagree_votes += voterStaked; 16 | } 17 | } else { 18 | proposal.total_votes -= votingInfo.staked; 19 | proposal.total_votes += voterStaked; 20 | if (votingInfo.is_agree === true) { 21 | if (updateVoting.is_agree === true) { 22 | proposal.agree_votes -= votingInfo.staked; 23 | proposal.agree_votes += voterStaked; 24 | } else { 25 | proposal.agree_votes -= votingInfo.staked; 26 | proposal.disagree_votes += voterStaked; 27 | } 28 | } else { 29 | if (updateVoting.is_agree === false) { 30 | proposal.disagree_votes -= votingInfo.staked; 31 | proposal.disagree_votes += voterStaked; 32 | } else { 33 | proposal.disagree_votes -= votingInfo.staked; 34 | proposal.agree_votes += voterStaked; 35 | } 36 | } 37 | } 38 | } 39 | 40 | module.exports = exports = vote; 41 | -------------------------------------------------------------------------------- /backend/libs/enum.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | VOTE_ACTION_VOTE : 1, 5 | VOTE_ACTION_UNVOTE : 2, 6 | VOTE_ACTION_STAKE : 3, 7 | VOTE_ACTION_UNSTAKE : 4, 8 | VOTE_ACTION_CHECK : 5, 9 | 10 | PROPOSAL_STATUS_PENDING : 1, 11 | PROPOSAL_STATUS_REJECTED : 2, 12 | PROPOSAL_STATUS_ON_VOTE : 3, 13 | PROPOSAL_STATUS_FINISHED_VOTING : 4, 14 | PROPOSAL_STATUS_CHECK_VOTE : 5, // check count of votes 15 | PROPOSAL_STATUS_CHECKED_VOTE : 6, // checked count of votes by platform 16 | PROPOSAL_STATUS_APPROVED : 7, // approve 17 | PROPOSAL_STATUS_COMPLETED : 8, 18 | 19 | // Redis Type 20 | REDIS_TYPE_BATCH_STATUS : 1, 21 | REDIS_TYPE_BATCH_CMD : 2, 22 | REDIS_TYPE_BATCH_CMD_ALL : 3, 23 | }; 24 | -------------------------------------------------------------------------------- /backend/libs/error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = { 4 | BAD_REQUEST : {error_code : 400, error_msg : 'Incorrect parameter'}, 5 | UNAUTHORIZED : {error_code : 401, error_msg : 'Authentication failed. Please sign in again'}, 6 | NOT_FOUND : {error_code : 404, error_msg : 'Not Found Error'}, 7 | NOT_ACCEPTABLE : {error_code : 406, error_msg : 'Not Acceptable'}, 8 | INTERNAL_SERVER_ERROR : {error_code : 500, error_msg : 'Internal Server Error'}, 9 | DUPLICATE_ERROR : {error_code : 11000, error_msg : 'Duplicate Error'}, 10 | }; 11 | -------------------------------------------------------------------------------- /backend/libs/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let winston = require('winston'), 4 | moment = require('moment'), 5 | DailyRotateFile = require('winston-daily-rotate-file'), 6 | config = require('config'); 7 | 8 | let timezone = config.timezone; 9 | 10 | let timestamp = function() { 11 | return moment().utcOffset(timezone).format(); 12 | }; 13 | 14 | config.webLog.timestamp = timestamp; 15 | config.dbLog.timestamp = timestamp; 16 | config.errLog.timestamp = timestamp; 17 | config.batchLog.timestamp = timestamp; 18 | config.batchErrLog.timestamp = timestamp; 19 | 20 | let webLogStream = { 21 | write: function(message, encoding) { 22 | webLog.info(message); 23 | } 24 | }; 25 | 26 | let webLog = new (winston.Logger)({ 27 | transports: [ 28 | //new (winston.transports.Console)({level : config.log.level}), 29 | new DailyRotateFile(config.webLog) 30 | ] 31 | }); 32 | 33 | let dbLog = new (winston.Logger)({ 34 | transports: [ 35 | new DailyRotateFile(config.dbLog) 36 | ] 37 | }); 38 | 39 | let errLog = new (winston.Logger)({ 40 | transports: [ 41 | new DailyRotateFile(config.errLog) 42 | ] 43 | }); 44 | 45 | let batchLog = new (winston.Logger)({ 46 | transports: [ 47 | new DailyRotateFile(config.batchLog) 48 | ] 49 | }); 50 | 51 | let batchErrLog = new (winston.Logger)({ 52 | transports: [ 53 | new DailyRotateFile(config.batchErrLog) 54 | ] 55 | }); 56 | 57 | config.dbLog = dbLog; 58 | 59 | module.exports = { 60 | webLogStream : webLogStream, 61 | webLog : webLog, 62 | dbLog : dbLog, 63 | errLog : errLog, 64 | batchLog : batchLog, 65 | batchErrLog : batchErrLog 66 | }; 67 | -------------------------------------------------------------------------------- /backend/libs/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | _ = require('lodash'), 5 | crypto = require('crypto'), 6 | request = require('request'), 7 | config = require('config'), 8 | SErr = require('libs/error'); 9 | 10 | let createHash = function(seed, salt) { 11 | let sha256 = crypto.createHash('sha256'); 12 | sha256.update(seed + salt); 13 | return sha256.digest('hex'); 14 | }; 15 | 16 | let createAccessToken = function(seed, salt) { 17 | return createHash(seed, salt + config.salt); 18 | }; 19 | 20 | let createErrResult = function(errorKey, extra) { 21 | let errorObj = Object.assign({}, SErr[errorKey]); 22 | errorObj.extra = extra; 23 | return {error : errorObj}; 24 | }; 25 | 26 | let createErrObject = function(errorKey, extra) { 27 | let err = new Promise.OperationalError(), 28 | errorObj = SErr[errorKey]; 29 | 30 | err.code = errorObj.error_code; 31 | err.message = errorObj.error_msg; 32 | err.errorKey = errorKey; 33 | 34 | if (!_.isNil(extra)) { 35 | err.extra = extra; 36 | } 37 | return err; 38 | }; 39 | 40 | function requestPro(options) { 41 | _.defaults(options, {timeout : 20000}); 42 | return new Promise(function(resolve, reject) { 43 | request(options, function(err, response, body) { 44 | if (!_.isEmpty(err)) { 45 | reject(err); 46 | } else { 47 | resolve([response, body]); 48 | } 49 | }); 50 | }); 51 | } 52 | 53 | function requestLoopPro(options, maxLoopCount, timeout, count) { 54 | count = count || 0; 55 | return requestPro(options) 56 | .catch(function(err) { 57 | if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') { 58 | if (count < maxLoopCount) { 59 | options.timeout = timeout; 60 | return requestLoopPro(options, maxLoopCount, timeout, ++count); 61 | } 62 | } 63 | throw err; 64 | }); 65 | } 66 | 67 | function toCamelCaseKey(map) { 68 | let ccMap = {}; 69 | _.forEach(map, function(value, key) { 70 | ccMap[_.camelCase(key)] = value; 71 | }); 72 | return ccMap; 73 | } 74 | 75 | function toAmount(eos) { 76 | const arr = eos.split(' '); 77 | return parseInt(arr[0] * 10000, 10); 78 | } 79 | 80 | module.exports = { 81 | createErrResult : createErrResult, 82 | createErrObject : createErrObject, 83 | createHash : createHash, 84 | createAccessToken : createAccessToken, 85 | requestPro : requestPro, 86 | requestLoopPro : requestLoopPro, 87 | toCamelCaseKey : toCamelCaseKey, 88 | toAmount : toAmount 89 | }; 90 | 91 | Promise.promisifyAll(module.exports); 92 | -------------------------------------------------------------------------------- /backend/log/.gitignore: -------------------------------------------------------------------------------- 1 | *.log.* 2 | -------------------------------------------------------------------------------- /backend/models/mongo/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'), 4 | _ = require('lodash'), 5 | config = require('config'), 6 | log = require('libs/log'); 7 | 8 | mongoose.Promise = require('bluebird'); 9 | 10 | mongoose.set('useFindAndModify', false); 11 | mongoose.set('useCreateIndex', true); 12 | 13 | const mongoConfig = config.mongo; 14 | const dbLog = log.dbLog; 15 | 16 | mongoose.set('debug', function(coll, method, query, doc, options) { 17 | dbLog.info({dbQuery : {coll, method, query, doc, options}}); 18 | }); 19 | 20 | function makeConnection(dbConfig) { 21 | var opts = null, 22 | uri = ''; 23 | if (!_.isEmpty(dbConfig.rsName)) { 24 | opts = { 25 | autoReconnect : true, 26 | poolSize: 30, 27 | rs_name : dbConfig.rsName, 28 | }; 29 | 30 | uri = makeReplSetUri(dbConfig); 31 | } else { 32 | opts = { 33 | autoReconnect : true, 34 | poolSize: 30, 35 | useNewUrlParser: true 36 | }; 37 | uri = makeUri(dbConfig); 38 | } 39 | return mongoose.createConnection(uri, opts); 40 | } 41 | 42 | //'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database 43 | function makeReplSetUri(dbConfig) { 44 | var arrUri = [], 45 | uri = null; 46 | 47 | uri = `mongodb://${dbConfig.account}:${dbConfig.passwd}@`; 48 | _.forEach(dbConfig.hosts, (host) => arrUri.push(host)); 49 | uri += arrUri.join(','); 50 | uri = `${uri}\${dbConfig.dbName}`; 51 | return uri; 52 | } 53 | 54 | //'mongodb://user:pass@localhost:port/database 55 | function makeUri(dbConfig) { 56 | return `mongodb://${dbConfig.account}:${dbConfig.passwd}@${dbConfig.host}/${dbConfig.dbName}`; 57 | } 58 | 59 | function createModel(constructor, conn) { 60 | return constructor(mongoose, conn); 61 | } 62 | 63 | const conn = makeConnection(mongoConfig); 64 | 65 | module.exports = exports = { 66 | User : createModel(require('models/mongo/user'), conn), 67 | WpsInfo : createModel(require('models/mongo/wps_info'), conn), 68 | LibAction : createModel(require('models/mongo/lib_action'), conn), 69 | LibActionStake : createModel(require('models/mongo/lib_action_stake'), conn), 70 | LibActionUnstake : createModel(require('models/mongo/lib_action_unstake'), conn), 71 | 72 | LibCommittee : createModel(require('models/mongo/lib_committee'), conn), 73 | LibProposal : createModel(require('models/mongo/lib_proposal'), conn), 74 | LibProposer : createModel(require('models/mongo/lib_proposer'), conn), 75 | LibReviewer : createModel(require('models/mongo/lib_reviewer'), conn), 76 | LibSummary : createModel(require('models/mongo/lib_summary'), conn), 77 | LibUpdateProposal : createModel(require('models/mongo/lib_update_proposal'), conn), 78 | LibUpdateVoting : createModel(require('models/mongo/lib_update_voting'), conn), 79 | LibVoterInfo : createModel(require('models/mongo/lib_voter_info'), conn), 80 | LibVotingInfo : createModel(require('models/mongo/lib_voting_info'), conn), 81 | }; 82 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_action.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | TrxSchema = new Schema({ 6 | trx_id : {type : String, index : true}, 7 | block_num : {type : Number, index : true}, 8 | account : {type : String, index : true}, 9 | name : {type : String, index : true}, 10 | authorization : [String], 11 | raw : Schema.Types.Mixed, 12 | created_at : {type : Date, default : Date.now} 13 | }); 14 | 15 | return conn.model('LibAction', TrxSchema); 16 | }; 17 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_action_stake.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | trx_id : {type : String, index : true}, 7 | from : {type : String, index : true}, 8 | receiver : {type : String}, 9 | stake_net_quantity : {type : String}, 10 | stake_cpu_quantity : {type : String}, 11 | created_at : {type : Date, default : Date.now} 12 | }); 13 | 14 | return conn.model('LibActionStake', schema); 15 | }; 16 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_action_unstake.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | trx_id : {type : String, index : true}, 7 | from : {type : String, index : true}, 8 | receiver : {type : String}, 9 | unstake_net_quantity : {type : String}, 10 | unstake_cpu_quantity : {type : String}, 11 | created_at : {type : Date, default : Date.now} 12 | }); 13 | 14 | return conn.model('LibActionUnstake', schema); 15 | }; 16 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_committee.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | committeeman : {type : String, index : true, unique : true}, 7 | category : {type : String}, 8 | is_oversight : {type : Boolean}, 9 | }); 10 | 11 | return conn.model('LibCommittee', schema); 12 | }; 13 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_proposal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | struct PROPOSAL_STATUS { 5 | const static uint8_t PENDING = 1; 6 | const static uint8_t REJECTED = 2; 7 | const static uint8_t ON_VOTE = 3; 8 | const static uint8_t FINISHED_VOTING = 4; 9 | const static uint8_t CHECK_VOTE = 5; // check count of votes 10 | const static uint8_t CHECKED_VOTE = 6; // checked count of votes by platform 11 | const static uint8_t APPROVED = 7; // approve 12 | const static uint8_t COMPLETED = 8; 13 | }; 14 | */ 15 | 16 | module.exports = exports = function(mongoose, conn) { 17 | const Schema = mongoose.Schema, 18 | schema = new Schema({ 19 | proposer : {type : String, index : true}, // proposer 20 | proposal_id : {type : Number, index : true}, // proposer id 21 | committee : {type : String, index : true}, // committee 22 | category : {type : String}, // category 23 | subcategory : {type : Number}, // subcategory 24 | title : {type : String}, // title 25 | summary : {type : String}, // summary 26 | project_img_url : {type : String}, // project image or video url 27 | description : {type : String}, // overview 28 | roadmap : {type : String}, // roadmap 29 | duration : {type : Number}, // duration 30 | members : [String], // linkedin 31 | funding_goal : {type : String}, // EOS 32 | total_votes : {type : Number, default : 0}, // total votes 33 | agree_votes : {type : Number, default : 0}, // agree votes 34 | disagree_votes : {type : Number, default : 0}, // disagree votes 35 | status : {type : Number, index : true}, // status 36 | vote_start_time : {type : Number}, // time when voting starts (seconds) 37 | fund_start_time : {type : Number}, // time when funding starts (seconds) 38 | iteration_of_funding : {type : Number} // number of iteration 39 | }); 40 | return conn.model('LibProposal', schema); 41 | }; 42 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_proposer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | account : {type : String, index : true, unique : true}, 7 | first_name : {type : String}, 8 | last_name : {type : String}, 9 | img_url : {type : String}, 10 | bio : {type : String}, 11 | country : {type : String}, 12 | telegram : {type : String}, 13 | website : {type : String}, 14 | linkedin : {type : String}, 15 | last_claim_time : {type : Number} 16 | }); 17 | 18 | return conn.model('LibProposer', schema); 19 | }; 20 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_reviewer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | account : {type : String, index : true, unique : true}, 7 | committee : {type : String}, 8 | first_name : {type : String}, 9 | last_name : {type : String} 10 | }); 11 | 12 | return conn.model('LibReviewer', schema); 13 | }; 14 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_summary.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | block_id : {type : String}, // 7 | block_num : {type : Number}, // 8 | total_voters : {type : Number, default : 0}, // 9 | total_proposals : {type : Number, default : 0}, // 10 | funded_proposals : {type : Number, default : 0}, // 11 | ongoing_proposals : {type : Number, default : 0} // 12 | }); 13 | return conn.model('LibSummary', schema); 14 | }; 15 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_update_proposal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | proposer : {type : String, index : true}, 7 | proposal_id : {type : Number, index : true} 8 | }); 9 | return conn.model('LibUpdateProposal', schema); 10 | }; 11 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_update_voting.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | account : {type : String, index : true}, 7 | proposal_id : {type : Number, index : true}, 8 | action : {type : Number}, 9 | is_agree : {type : Boolean} 10 | }); 11 | schema.index({account : 1, proposal_id : 1}); 12 | return conn.model('LibUpdateVoting', schema); 13 | }; 14 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_voter_info.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | account : {type : String, index : true}, 7 | staked : {type : Number}, 8 | }, {timestamps : true}); 9 | 10 | return conn.model('LibVoterInfo', schema); 11 | }; 12 | -------------------------------------------------------------------------------- /backend/models/mongo/lib_voting_info.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | proposal_id : {type : Number, index : true}, 7 | account : {type : String, index : true}, 8 | staked : {type : Number}, 9 | is_agree : {type : Boolean} 10 | }); 11 | 12 | schema.index({proposal_id : 1, account : 1}, {unique: true}); 13 | 14 | return conn.model('LibVotingInfo', schema); 15 | }; 16 | -------------------------------------------------------------------------------- /backend/models/mongo/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | eos_account : {type : String, index : true, unique : true, minlength : 1, maxlength : 12}, 7 | nick : {type : String, minlength : 1, maxlength : 32}, 8 | img_url : {type : String, maxlength : 128}, 9 | bio : {type : String, maxlength : 256}, 10 | location : {type : String, maxlength : 50}, 11 | telegram : {type : String, maxlength : 64}, 12 | website : {type : String, maxlength : 128}, 13 | linkedin : {type : String, maxlength : 128} 14 | }, {timestamps : true}); 15 | return conn.model('User', schema); 16 | }; 17 | -------------------------------------------------------------------------------- /backend/models/mongo/wps_info.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function(mongoose, conn) { 4 | const Schema = mongoose.Schema, 5 | schema = new Schema({ 6 | watchman_account : {type : String}, 7 | watchman_prv : {type : String}, 8 | watchman_pub : {type : String}, 9 | total_voting_percent : {type : Number}, 10 | agree_percent : {type : Number} 11 | }, {timestamps : true}); 12 | return conn.model('WpsInfo', schema); 13 | }; 14 | -------------------------------------------------------------------------------- /backend/models/redis/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'), 4 | SEnum = require('libs/enum'), 5 | RedisClient = require('./redis_client'), 6 | config = require('config').redis; 7 | 8 | RedisClient.key = function(type, keyIds, version) { 9 | let keyStr = _key(type, keyIds); 10 | if (!_.isNil(version)) { 11 | keyStr = `${keyStr}:v:${version}`; 12 | } 13 | return keyStr; 14 | }; 15 | 16 | RedisClient.expire = function(type) { 17 | var expireTime = config.expire.default; 18 | switch (type) { 19 | // auth 20 | case SEnum.REDIS_TYPE_PRODUCER_SESSION: 21 | expireTime = config.expire.redis_admin_session; 22 | break; 23 | } 24 | return expireTime || config.expire.default; 25 | }; 26 | 27 | function _key(type, keyIds) { 28 | switch (type) { 29 | case SEnum.REDIS_TYPE_BATCH_STATUS: 30 | return `eos:batch:status:${keyIds[0]}`; // job name 31 | case SEnum.REDIS_TYPE_BATCH_CMD: 32 | return `eos:batch:cmd:${keyIds[0]}`; // job name 33 | case SEnum.REDIS_TYPE_BATCH_CMD_ALL: 34 | return 'eos:batch:cmd:all'; 35 | 36 | case SEnum.REDIS_TYPE_FRONTEND_MANIFEST: // front-end 37 | return 'm:frontend:manifest'; 38 | 39 | } 40 | return 'none'; 41 | } 42 | 43 | module.exports = RedisClient; 44 | -------------------------------------------------------------------------------- /backend/models/redis/redis_client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | redis = require('redis'), 5 | _ = require('lodash'), 6 | HashRing = require('hashring'), 7 | redisConfig = require('config').redis; 8 | 9 | function RedisLoadBalance(redis, arrHost, defaultPort) { 10 | if (!(this instanceof RedisLoadBalance)) { 11 | return new RedisLoadBalance(arrHost); 12 | } 13 | let self = this; 14 | self.mapClient = {}; 15 | if (_.isArray(arrHost) === true) { 16 | self.hashRing = new HashRing(arrHost); 17 | _.forEach(arrHost, function(host) { 18 | let arrHostInfo = host.split(':'), 19 | port = arrHostInfo.length > 1 ? arrHostInfo[1] : defaultPort; 20 | self.mapClient[host] = Promise.promisifyAll(redis.createClient(port, arrHostInfo[0])); 21 | }); 22 | } else { 23 | self.mapClient[arrHost] = Promise.promisifyAll(redis.createClient(defaultPort, arrHost)); 24 | } 25 | self.hosts = arrHost; 26 | } 27 | 28 | RedisLoadBalance.prototype.getClient = function(key) { 29 | let host = ''; 30 | if (_.isArray(this.hosts) === true) { 31 | host = this.hashRing.get(key); 32 | } else { 33 | host = this.hosts; 34 | } 35 | return this.mapClient[host]; 36 | }; 37 | 38 | RedisLoadBalance.prototype.getClients = function() { 39 | return this.mapClient; 40 | }; 41 | 42 | function RedisClient(type, keyIds, version) { 43 | if (!(this instanceof RedisClient)) { 44 | return new RedisClient(type, keyIds); 45 | } 46 | this.type = type; 47 | this.keyIds = keyIds || [0]; 48 | this.value = ''; 49 | this.client = RedisClient.getClient(RedisClient.key(type, keyIds)); 50 | this.version = version; 51 | } 52 | 53 | RedisClient.initLoadBalance = function(redis, hosts, defaultPort) { 54 | RedisClient.loadBalance = new RedisLoadBalance(redis, hosts, defaultPort); 55 | }; 56 | 57 | RedisClient.getClient = function(key) { 58 | return RedisClient.loadBalance.getClient(key); 59 | }; 60 | 61 | RedisClient.getClients = function() { 62 | return RedisClient.loadBalance.getClients(); 63 | }; 64 | 65 | RedisClient.key = function(type, keyIds, version) { 66 | }; 67 | 68 | RedisClient.expire = function(type) { 69 | }; 70 | 71 | RedisClient.remove = function(type, keyIds, version, callback) { 72 | keyIds = keyIds || []; 73 | let key = RedisClient.key(type, keyIds, version), 74 | client = RedisClient.getClient(key); 75 | client.del(key, function(err, res) { 76 | if (err) { 77 | callback(err, res); 78 | return; 79 | } 80 | callback(err, true); 81 | }); 82 | }; 83 | 84 | RedisClient.removeByPatternPro = function(pattern) { 85 | let clients = RedisClient.getClients(); 86 | let arrRedis = _.map(clients, (redis, key) => redis); 87 | return Promise.each(arrRedis, (redis) => { 88 | return redis.keysAsync(pattern) 89 | .then((keys) => { 90 | if (_.isEmpty(keys)) { 91 | return null; 92 | } 93 | return redis.delAsync(keys); 94 | }); 95 | }); 96 | }; 97 | 98 | RedisClient.genScore = function genRedisScore(anchorId, size, direction) { 99 | let score = parseInt(anchorId) * 4000 + parseInt(size); 100 | if (direction === 'past') { 101 | score = score * (-1); 102 | } 103 | return score; 104 | }; 105 | 106 | 107 | RedisClient.prototype.remove = function(callback) { 108 | let key = RedisClient.key(this.type, this.keyIds, this.version); 109 | this.client.del(key, function(err, res) { 110 | if (err) { 111 | callback(err, res); 112 | return; 113 | } 114 | callback(err, true); 115 | }); 116 | }; 117 | 118 | RedisClient.prototype.save = function(callback) { 119 | let self = this, 120 | key = RedisClient.key(self.type, self.keyIds, self.version); 121 | self.client.set(key, this.value, 122 | function(err, res) { 123 | if (err) { 124 | callback(err, res); 125 | return; 126 | } 127 | let result = true, 128 | expireTime = RedisClient.expire(self.type); 129 | if (res !== 'OK') { 130 | result = false; 131 | } 132 | if (!_.isNil(expireTime)) { 133 | self.client.expire(key, expireTime, function(err, res) { 134 | callback(err, result); 135 | }); 136 | } else { 137 | callback(err, result); 138 | } 139 | }); 140 | }; 141 | 142 | RedisClient.prototype.restore = function(callback) { 143 | let self = this, 144 | key = RedisClient.key(self.type, self.keyIds, self.version); 145 | self.client.get(key, function(err, value) { 146 | if (err) { 147 | callback(err, value); 148 | return; 149 | } 150 | self.value = value; 151 | callback(err, self.value); 152 | }); 153 | }; 154 | 155 | RedisClient.prototype.saveSorted = function(score, callback) { 156 | let self = this, 157 | key = RedisClient.key(self.type, self.keyIds, self.version); 158 | this.client.zadd(key, score, this.value, 159 | function(err, res) { 160 | if (err) { 161 | callback(err, res); 162 | return; 163 | } 164 | let result = true, 165 | expireTime = RedisClient.expire(self.type); 166 | if (res !== 'OK') { 167 | result = false; 168 | } 169 | if (!_.isNil(expireTime)) { 170 | self.client.expire(key, expireTime, function(err, res) { 171 | callback(err, result); 172 | }); 173 | } else { 174 | callback(err, result); 175 | } 176 | }); 177 | }; 178 | 179 | RedisClient.prototype.restoreSorted = function(score, callback) { 180 | let self = this, 181 | key = RedisClient.key(self.type, self.keyIds, self.version), 182 | start = score, 183 | stop = '(' + (score + 1); 184 | self.client.zrangebyscore(key, start, stop, function(err, value) { 185 | if (err) { 186 | callback(err, value); 187 | return; 188 | } 189 | self.value = value; 190 | callback(err, self.value); 191 | }); 192 | }; 193 | 194 | RedisClient.prototype.restoreSortedRange = function(start, stop, callback) { 195 | let self = this, 196 | key = RedisClient.key(self.type, self.keyIds, self.version); 197 | self.client.zrangebyscore(key, start, stop, function(err, value) { 198 | if (err) { 199 | callback(err, value); 200 | return; 201 | } 202 | self.value = value; 203 | callback(err, self.value); 204 | }); 205 | }; 206 | 207 | RedisClient.prototype.restoreSortedMaxScore = function(callback) { 208 | let self = this, 209 | key = RedisClient.key(self.type, self.keyIds, self.version); 210 | let args = [ key, '+inf', '-inf', 'WITHSCORES', 'LIMIT', 0, 1]; 211 | self.client.zrevrangebyscore(args, function(err, value) { 212 | if (err) { 213 | callback(err, value); 214 | return; 215 | } 216 | self.value = value; 217 | callback(err, self.value); 218 | }); 219 | }; 220 | 221 | RedisClient.initLoadBalance(redis, redisConfig.hosts, redisConfig.port); 222 | 223 | Promise.promisifyAll(RedisClient); 224 | Promise.promisifyAll(RedisClient.prototype); 225 | 226 | module.exports = RedisClient; 227 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wps-server", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "deploy": "NODE_ENV=developer NODE_PATH=. node tools/deploy_front_end.js", 8 | "test": "NODE_ENV=developer NODE_PATH=. mocha --recursive -R list" 9 | }, 10 | "dependencies": { 11 | "binaryen": "^37.0.0", 12 | "bluebird": "^3.5.1", 13 | "config": "^2.0.0", 14 | "cookie-parser": "~1.4.3", 15 | "cors": "^2.8.4", 16 | "crypto": "^1.0.1", 17 | "debug": "~2.6.9", 18 | "eosjs": "^16.0.5", 19 | "express": "~4.16.0", 20 | "hashring": "^3.2.0", 21 | "http-errors": "~1.6.2", 22 | "jsonwebtoken": "^8.3.0", 23 | "jszip": "^3.1.5", 24 | "lodash": "^4.17.10", 25 | "mime": "^2.3.1", 26 | "moment": "^2.22.1", 27 | "mongoose": "^5.2.9", 28 | "morgan": "~1.9.0", 29 | "multer": "^1.3.1", 30 | "node-schedule": "^1.3.0", 31 | "numeral": "^2.0.6", 32 | "pug": "^2.0.3", 33 | "redis": "^2.8.0", 34 | "request": "^2.87.0", 35 | "s3fs": "^2.5.0", 36 | "socket.io": "^2.1.1", 37 | "superagent": "^3.8.3", 38 | "supertest": "^3.1.0", 39 | "unzipper": "^0.8.14", 40 | "winston": "^2.4.3", 41 | "winston-daily-rotate-file": "^3.3.1" 42 | }, 43 | "devDependencies": { 44 | "chai": "^4.1.2", 45 | "sinon": "^6.1.5", 46 | "sinon-mongoose": "^2.2.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/readme.md: -------------------------------------------------------------------------------- 1 | # wps backend server 2 | ## Version : 1.0.0 3 | 4 | ## Server Info 5 | ### required 6 | mongodb 7 | redis 8 | nodejs 9 | ### development server info 10 | http://http://wps-test.hkeos.com 11 | 12 | ex) 13 | http://wps-test.hkeos.com/api/1/summary 14 | 15 | ### production server info 16 | Coming soon 17 | 18 | ## start 19 | ### npm install 20 | npm install 21 | npm install -g pm2 22 | 23 | ### edit nodeos, mongo, redis environment 24 | vi config/developer.json 25 | vi tools/create_wps_info.js 26 | NODE_ENV=developer NODE_PATH=. node tools/create_wps_info.js 27 | 28 | ### start pm2 29 | pm2 start deploy/{eco_xxx.json} 30 | 31 | ### stop pm2 32 | pm2 kill 33 | 34 | ## API 35 | The format of the returned value is json. 36 | 37 | ### stats 38 | GET /api/1/summary 39 | Returns summary object 40 | return 41 | summary 42 | 43 | ### user 44 | POST /api/1/users 45 | This method create user account. 46 | 47 | body parameter 48 | eos_account (string max 12, required) : eos account, 49 | nick (string max 32, required) : nick name, 50 | img_url (string max 128): profile image url, 51 | bio (string max 256): bio, 52 | location (string max 50): location, 53 | telegram (string max 64): telegram username, 54 | website (string max 128): website url, 55 | linkedin (string max 128): linkedin account 56 | retun 57 | user : { 58 | _id (string) : mongo Object Id, 59 | eos_account (string) : eos account, 60 | nick (string) : nick name, 61 | img_url (string): profile image url, 62 | bio (string): bio, 63 | location (string): location, 64 | telegram (string): telegram username, 65 | website (string): website url, 66 | linkedin (string): linkedin account 67 | } 68 | 69 | ex) 70 | eos_account : 'useraaaaaaam', 71 | nick : 'yepps', 72 | img_url : 'https://i.amz.mshcdn.com/3NbrfEiECotKyhcUhgPJHbrL7zM=/950x534/filters:quality(90)/2014%2F06%2F02%2Fc0%2Fzuckheadsho.a33d0.jpg', 73 | bio : 'Rodney Erickson is a content marketing professional at HubSpot, an inbound marketing and sales platform that helps companies attract visitors, convert leads, and close customers. Previously, Rodney worked as a marketing manager for a tech software startup.', 74 | location : 'USA, WA', 75 | telegram : 'yepp4you', 76 | website : 'www.google.com', 77 | linkedin : 'yepp4you' 78 | 79 | PUT /api/1/users/{eos_account} 80 | This method update user account. 81 | params 82 | eos_account : eos account 83 | body parameter 84 | nick (string max 32, required) : nick name, 85 | img_url (string max 128): profile image url, 86 | bio (string max 256): bio, 87 | location (string max 50): location, 88 | telegram (string max 64): telegram username, 89 | website (string max 128): website url, 90 | linkedin (string max 128): linkedIn account 91 | return 92 | user 93 | 94 | GET /api/1/users/{eos_account} 95 | Returns user object. 96 | params : 97 | eos_accout : eos account 98 | return 99 | user 100 | 101 | 102 | 103 | ### proposal 104 | GET /api/1/committees/{committee}/proposals/orders/{order} 105 | Returns proposal object list order by `order parameter` 106 | params : 107 | committee (string): eos acount of committee 108 | order (string): 'hot' or 'latest' 109 | return 110 | proposals : [proposal object] 111 | 112 | GET /api/1/committees/{committee}/proposals/{proposal_id} 113 | Returns proposal object 114 | params 115 | committee (string) : eos committee acount name 116 | proposal_id (number) : proposal's id 117 | return 118 | proposal 119 | 120 | ### proposer 121 | GET /api/1/proposers/{proposer}/proposals 122 | Returns proposal object list of proposer 123 | params 124 | proposer : eos account of proposer 125 | return 126 | proposals : [proposal object] 127 | 128 | ### committee 129 | GET /api/1/committees 130 | Returns committee object list (you can get category list) 131 | return 132 | committees : [committee object] 133 | 134 | ### object 135 | summary 136 | { 137 | _id (string) : mongo object id 138 | block_id (string) : block id 139 | block_num (number) : block number 140 | funded_proposals (number) : number of funded proposal 141 | ongoing_proposals (number) : number of ongoing proposal 142 | total_proposals (number ) : number of total proposal 143 | total_voters (number) : number of total voter 144 | } 145 | 146 | committee 147 | { 148 | _id (string) : mongo object id 149 | committeeman (string) : eos account of committee 150 | category (string) : category name of committee (ex. emergency) 151 | is_oversight (boolean) : if true, have oversight right, otherwise not oversight right 152 | } 153 | 154 | user 155 | { 156 | _id (string) : mongo ObjectId 157 | eos_account (string) : eos account 158 | nick (string) : nick name 159 | img_url (string) : profile image url 160 | bio (string) : bio 161 | location (string) : location 162 | telegram (string) : telegram username 163 | website (string) : website url 164 | linkedin (string): linkedin account 165 | } 166 | 167 | proposal 168 | { 169 | _id (string) : mongo ObjectId 170 | proposer (string) : proposer 171 | proposal_id (number) : proposer id 172 | committee (string) : committee 173 | category (string) : category 174 | subcategory (number) : subcategory 175 | title (string) : title 176 | summary (string) : summary 177 | project_img_url (string) : project image or video url 178 | description (string) : overview 179 | roadmap (string) : roadmap 180 | duration (number) : duration 181 | members ([string]) : linkedin 182 | funding_goal (string) : EOS 183 | total_votes (number) : total votes 184 | agree_votes (number) :, agree votes 185 | disagree_votes (number) : disagree votes 186 | status (number) : status (1 : pending, 2 : reject, 3 : on vote, 4 : check vote, 5 : checked vote, 6 : approved, 7 : completed) 187 | vote_start_time (number) : time when voting starts (seconds) 188 | fund_start_time (number) : time when funding starts (seconds) 189 | iteration_of_funding (number) : number of funding iteration 190 | } 191 | -------------------------------------------------------------------------------- /backend/routes/api/committee.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'), 4 | log = require('libs/log'), 5 | Committee = require('libs/api/committee'), 6 | SUtil = require('libs/util'), 7 | SErr = require('libs/error'); 8 | 9 | const errLog = log.errLog; 10 | const router = express.Router(); 11 | 12 | let getCommittees = function(req, res, next) { 13 | Committee.getCommittees() 14 | .then(function(committees) { 15 | res.json({committees}); 16 | }) 17 | .catch(function(err) { 18 | errorRes(res, err); 19 | }); 20 | 21 | }; 22 | 23 | function errorRes(res, err) { 24 | errLog.info(err); 25 | errLog.info(err.stack); 26 | 27 | if (err.code === SErr.BAD_REQUEST.error_code) { 28 | res.json(SUtil.createErrResult('BAD_REQUEST')); 29 | } else if (err.code === SErr.NOT_ACCEPTABLE.error_code) { 30 | res.json(SUtil.createErrResult('NOT_ACCEPTABLE')); 31 | } else if (err.code === SErr.DUPLICATE_ERROR.error_code) { 32 | res.json(SUtil.createErrResult('DUPLICATE_ERROR')); 33 | } else if (err.code === SErr.NODE_INVALIDATE_ERROR.error_code) { 34 | res.json(SUtil.createErrResult('NODE_INVALIDATE_ERROR')); 35 | } 36 | else { 37 | res.json(SUtil.createErrResult('INTERNAL_SERVER_ERROR', err.error)); 38 | } 39 | } 40 | 41 | router.get('/1/committees', getCommittees); 42 | 43 | module.exports = exports = router; 44 | -------------------------------------------------------------------------------- /backend/routes/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let useRoutes = function(app) { 4 | const committeeRouter = require('./committee'); 5 | const proposerRouter = require('./proposer'); 6 | const proposalRouter = require('./proposal'); 7 | const userRouter = require('./user'); 8 | const statsRouter = require('./stats'); 9 | 10 | app.use('/api/', committeeRouter); 11 | app.use('/api/', proposerRouter); 12 | app.use('/api/', proposalRouter); 13 | app.use('/api/', userRouter); 14 | app.use('/api/', statsRouter); 15 | }; 16 | 17 | module.exports = exports = { 18 | useRoutes : useRoutes 19 | }; 20 | -------------------------------------------------------------------------------- /backend/routes/api/proposal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'), 4 | log = require('libs/log'), 5 | Proposal = require('libs/api/proposal'), 6 | SUtil = require('libs/util'), 7 | SErr = require('libs/error'); 8 | 9 | const errLog = log.errLog; 10 | const router = express.Router(); 11 | 12 | let getProposals = function(req, res, next) { 13 | Proposal.getProposals(req.params) 14 | .then(function(proposals) { 15 | res.json({proposals}); 16 | }) 17 | .catch(function(err) { 18 | errorRes(res, err); 19 | }); 20 | 21 | }; 22 | 23 | let getProposal = function(req, res, next) { 24 | const proposalId = parseInt(req.params.proposal_id); 25 | Proposal.getProposal(proposalId) 26 | .then(function(proposal) { 27 | res.json({proposal}); 28 | }) 29 | .catch(function(err) { 30 | errorRes(res, err); 31 | }); 32 | }; 33 | 34 | 35 | function errorRes(res, err) { 36 | errLog.info(err); 37 | errLog.info(err.stack); 38 | 39 | if (err.code === SErr.BAD_REQUEST.error_code) { 40 | res.json(SUtil.createErrResult('BAD_REQUEST')); 41 | } else if (err.code === SErr.NOT_ACCEPTABLE.error_code) { 42 | res.json(SUtil.createErrResult('NOT_ACCEPTABLE')); 43 | } else if (err.code === SErr.DUPLICATE_ERROR.error_code) { 44 | res.json(SUtil.createErrResult('DUPLICATE_ERROR')); 45 | } else if (err.code === SErr.NODE_INVALIDATE_ERROR.error_code) { 46 | res.json(SUtil.createErrResult('NODE_INVALIDATE_ERROR')); 47 | } 48 | else { 49 | res.json(SUtil.createErrResult('INTERNAL_SERVER_ERROR', err.error)); 50 | } 51 | } 52 | 53 | router.get('/1/committees/:committee/proposals/orders/:order', getProposals); 54 | router.get('/1/committees/:committee/proposals/:proposal_id', getProposal); 55 | 56 | module.exports = exports = router; 57 | -------------------------------------------------------------------------------- /backend/routes/api/proposer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'), 4 | log = require('libs/log'), 5 | Proposer = require('libs/api/proposer'), 6 | SUtil = require('libs/util'), 7 | SErr = require('libs/error'); 8 | 9 | const errLog = log.errLog; 10 | const router = express.Router(); 11 | 12 | let getProposals = function(req, res, next) { 13 | const proposer = req.params.proposer; 14 | Proposer.getProposals(proposer) 15 | .then(function(proposals) { 16 | res.json({proposals}); 17 | }) 18 | .catch(function(err) { 19 | errorRes(res, err); 20 | }); 21 | }; 22 | 23 | function errorRes(res, err) { 24 | errLog.info(err); 25 | errLog.info(err.stack); 26 | 27 | if (err.code === SErr.BAD_REQUEST.error_code) { 28 | res.json(SUtil.createErrResult('BAD_REQUEST')); 29 | } else if (err.code === SErr.NOT_ACCEPTABLE.error_code) { 30 | res.json(SUtil.createErrResult('NOT_ACCEPTABLE')); 31 | } else if (err.code === SErr.DUPLICATE_ERROR.error_code) { 32 | res.json(SUtil.createErrResult('DUPLICATE_ERROR')); 33 | } else if (err.code === SErr.NODE_INVALIDATE_ERROR.error_code) { 34 | res.json(SUtil.createErrResult('NODE_INVALIDATE_ERROR')); 35 | } 36 | else { 37 | res.json(SUtil.createErrResult('INTERNAL_SERVER_ERROR', err.error)); 38 | } 39 | } 40 | 41 | router.get('/1/proposers/:proposer/proposals', getProposals); 42 | 43 | module.exports = exports = router; 44 | -------------------------------------------------------------------------------- /backend/routes/api/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'), 4 | log = require('libs/log'), 5 | Stats = require('libs/api/stats'), 6 | SUtil = require('libs/util'), 7 | SErr = require('libs/error'); 8 | 9 | const errLog = log.errLog; 10 | const router = express.Router(); 11 | 12 | let getSummary = function(req, res, next) { 13 | Stats.getSummary() 14 | .then(function(summary) { 15 | res.json({summary}); 16 | }) 17 | .catch(function(err) { 18 | errorRes(res, err); 19 | }); 20 | }; 21 | 22 | function errorRes(res, err) { 23 | errLog.info(err); 24 | errLog.info(err.stack); 25 | 26 | if (err.code === SErr.BAD_REQUEST.error_code) { 27 | res.json(SUtil.createErrResult('BAD_REQUEST')); 28 | } else if (err.code === SErr.NOT_ACCEPTABLE.error_code) { 29 | res.json(SUtil.createErrResult('NOT_ACCEPTABLE')); 30 | } else if (err.code === SErr.DUPLICATE_ERROR.error_code) { 31 | res.json(SUtil.createErrResult('DUPLICATE_ERROR')); 32 | } else if (err.code === SErr.NODE_INVALIDATE_ERROR.error_code) { 33 | res.json(SUtil.createErrResult('NODE_INVALIDATE_ERROR')); 34 | } 35 | else { 36 | res.json(SUtil.createErrResult('INTERNAL_SERVER_ERROR', err.error)); 37 | } 38 | } 39 | 40 | router.get('/1/summary', getSummary); 41 | 42 | module.exports = exports = router; 43 | -------------------------------------------------------------------------------- /backend/routes/api/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'), 4 | log = require('libs/log'), 5 | User = require('libs/api/user'), 6 | SUtil = require('libs/util'), 7 | SErr = require('libs/error'); 8 | 9 | const errLog = log.errLog; 10 | const router = express.Router(); 11 | 12 | let createUser = function(req, res, next) { 13 | User.createUser(req.body) 14 | .then(function(user) { 15 | res.json({user}); 16 | }) 17 | .catch(function(err) { 18 | errorRes(res, err); 19 | }); 20 | }; 21 | 22 | let updateUser = function(req, res, next) { 23 | User.updateUser(req.body) 24 | .then(function(user) { 25 | res.json({user}); 26 | }) 27 | .catch(function(err) { 28 | errorRes(res, err); 29 | }); 30 | }; 31 | 32 | let getUser = function(req, res, next) { 33 | User.getUser(req.params.eos_account) 34 | .then(function(user) { 35 | res.json({user}); 36 | }) 37 | .catch(function(err) { 38 | errorRes(res, err); 39 | }); 40 | }; 41 | 42 | let deleteUser = function(req, res, next) { 43 | User.deleteUser(req.params.eos_account) 44 | .then(function(result) { 45 | res.json({result : 'OK', eos_account : req.params.eos_account}); 46 | }) 47 | .catch(function(err) { 48 | errorRes(res, err); 49 | }); 50 | }; 51 | 52 | function errorRes(res, err) { 53 | errLog.info(JSON.stringify(err)); 54 | errLog.info(err.stack); 55 | 56 | if (err.code === SErr.BAD_REQUEST.error_code) { 57 | res.json(SUtil.createErrResult('BAD_REQUEST')); 58 | } else if (err.code === SErr.NOT_ACCEPTABLE.error_code) { 59 | res.json(SUtil.createErrResult('NOT_ACCEPTABLE')); 60 | } else if (err.code === SErr.DUPLICATE_ERROR.error_code) { 61 | res.json(SUtil.createErrResult('DUPLICATE_ERROR')); 62 | } 63 | else { 64 | res.json(SUtil.createErrResult('INTERNAL_SERVER_ERROR', err.error)); 65 | } 66 | } 67 | 68 | router.post('/1/users', createUser); 69 | router.put('/1/users/:eos_account', updateUser); 70 | router.get('/1/users/:eos_account', getUser); 71 | router.delete('/1/users/:eos_account', deleteUser); 72 | 73 | module.exports = exports = router; 74 | -------------------------------------------------------------------------------- /backend/scripts/cleos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################################################ 3 | # 4 | # Scrip Created by http://CryptoLions.io 5 | # For EOS mainnet 6 | # 7 | # https://github.com/CryptoLions/EOS-MainNet 8 | # 9 | ############################################################################### 10 | 11 | NODEHOST="user-api.eoseoul.io" 12 | NODEPORT="80" 13 | 14 | WALLETHOST="127.0.0.1" 15 | WALLETPORT="8900" 16 | 17 | echo NODE HOST : $NODEHOST:$NODEPORT 18 | 19 | cleos -u http://$NODEHOST:$NODEPORT --wallet-url http://$WALLETHOST:$WALLETPORT "$@" 20 | -------------------------------------------------------------------------------- /backend/scripts/cleos_dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################################################ 3 | # 4 | # Scrip Created by http://CryptoLions.io 5 | # For EOS mainnet 6 | # 7 | # https://github.com/CryptoLions/EOS-MainNet 8 | # 9 | ############################################################################### 10 | 11 | NODEHOST="127.0.0.1" 12 | NODEPORT="8888" 13 | 14 | WALLETHOST="127.0.0.1" 15 | WALLETPORT="8888" 16 | 17 | echo NODE HOST : $NODEHOST:$NODEPORT 18 | 19 | cleos -u http://$NODEHOST:$NODEPORT --wallet-url http://$WALLETHOST:$WALLETPORT "$@" 20 | -------------------------------------------------------------------------------- /backend/test/batch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('[batch]', function() { 4 | require('./update_votings')(); 5 | require('./update_proposals')(); 6 | require('./update_summary')(); 7 | }); 8 | -------------------------------------------------------------------------------- /backend/test/batch/update_proposals/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function() { 4 | describe('update_proposals', () => { 5 | const sinon = require('sinon'), 6 | SEnum = require('libs/enum'), 7 | mongo = require('models/mongo'), 8 | eosApi = require('external_apis/eos_api'); 9 | 10 | require('sinon-mongoose'); 11 | require('chai').should(); 12 | 13 | const updateProposals = require('libs/batch/update_proposals').updateProposals; 14 | 15 | const Proposal = mongo.LibProposal; 16 | const UpdateProposal = mongo.LibUpdateProposal; 17 | /* 18 | PROPOSAL_STATUS_PENDING : 1, 19 | PROPOSAL_STATUS_REJECTED : 2, 20 | PROPOSAL_STATUS_ON_VOTE : 3, 21 | PROPOSAL_STATUS_FINISHED_VOTING : 4, 22 | PROPOSAL_STATUS_CHECK_VOTE : 5, // check count of votes 23 | PROPOSAL_STATUS_CHECKED_VOTE : 6, // checked count of votes by platform 24 | PROPOSAL_STATUS_APPROVED : 7, // approve 25 | PROPOSAL_STATUS_COMPLETED : 8, 26 | */ 27 | const proposal = { 28 | _id : '5b7bc8ec9b53c169b2caa48c', 29 | proposer: 'xxxxxxxxxxx', 30 | proposal_id : 1, 31 | committee: 'committeeaaa', 32 | category: 'emergency', 33 | subcategory: 1, 34 | title: 'wps project title', 35 | summary: 'wps proejct summary', 36 | project_img_url: 'http://www.google.com', 37 | description: 38 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 39 | roadmap: 40 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 41 | duration: 30, 42 | members: [ 'yepp4you1', 'yepp4you2', 'yepp4you3' ], 43 | funding_goal: '10.0000 EOS', 44 | total_votes: 10000000, 45 | agree_votes: 5200000, 46 | disagree_votes: 4800000, 47 | status: SEnum.PROPOSAL_STATUS_PENDING, 48 | vote_start_time: 0, 49 | fund_start_time: 0, 50 | iteration_of_funding: 0 51 | }; 52 | 53 | const proposalFromEOS = { 54 | proposer: 'xxxxxxxxxxx', 55 | id : 1, 56 | committee: 'committeeaaa', 57 | category: 'emergency', 58 | subcategory: 1, 59 | title: 'wps project title from EOS', 60 | summary: 'wps proejct summary', 61 | project_img_url: 'http://www.google.com', 62 | description: 63 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 64 | roadmap: 65 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 66 | duration: 30, 67 | members: [ 'yepp4you1', 'yepp4you2', 'yepp4you3' ], 68 | funding_goal: '10.0000 EOS', 69 | total_votes: 10000000, 70 | agree_votes: 5200000, 71 | disagree_votes: 4800000, 72 | status: SEnum.PROPOSAL_STATUS_CHECK_VOTE, 73 | vote_start_time: 1534733451, 74 | fund_start_time: 0, 75 | iteration_of_funding: 0 76 | }; 77 | 78 | const chainInfo = { 79 | server_version: 'bf28f8bb', 80 | chain_id: 'a4fe43c897279a677025624555f3835b949f995f87923c97f0392dcff835bfd0', 81 | head_block_num: 208607, 82 | last_irreversible_block_num: 208271, 83 | last_irreversible_block_id: '00032d8fd0bb3ca9d0b46dec6be826d2d57b06ee569abebcfbb4ffda105624de', 84 | head_block_id: '00032edf764f42f9f30dfce850ef60ac46a1b0c14eb90dd595da457c1bc14e8c', 85 | head_block_time: '2018-08-22T09:40:17.500', 86 | head_block_producer: 'producer111h', 87 | virtual_block_cpu_limit: 200000000, 88 | virtual_block_net_limit: 1048576000, 89 | block_cpu_limit: 199900, 90 | block_net_limit: 1048576, 91 | server_version_string: 'v1.2.1-dirty' 92 | }; 93 | 94 | const sandbox = sinon.createSandbox(); 95 | describe('vote', () => { 96 | beforeEach(() => { 97 | sinon.mock(Proposal) 98 | .expects('findOne') 99 | .atLeast(1) 100 | .atMost(10) 101 | .withArgs({proposal_id : 1, status : 2}) 102 | .resolves(Object.assign({}, proposal)); 103 | 104 | sandbox.replace(eosApi, 'getInfo', async function() { 105 | return chainInfo; 106 | }); 107 | 108 | sandbox.replace(eosApi, 'getProposalByOwner', async function(proposer) { 109 | return Object.assign({}, proposalFromEOS); 110 | }); 111 | 112 | sandbox.replace(eosApi, 'getProposalById', async function(proposalId) { 113 | return Object.assign({}, proposalFromEOS); 114 | }); 115 | 116 | sandbox.replace(eosApi, 'getRejectedProposalById', async function(proposalId) { 117 | return Object.assign({}, proposalFromEOS); 118 | }); 119 | 120 | sandbox.replace(eosApi, 'getFinishedProposalById', async function(proposalId) { 121 | return Object.assign({}, proposalFromEOS); 122 | }); 123 | 124 | sandbox.replace(eosApi, 'checkExpire', async function(params, key, authorization, endpoint) { 125 | return {}; 126 | }); 127 | }); 128 | 129 | afterEach(() => { 130 | sinon.restore(); 131 | sandbox.restore(); 132 | }); 133 | 134 | it('create', async function() { 135 | sandbox.replace(Proposal, 'updateOne', async function(condition, data) { 136 | // console.log(data); 137 | // The following values must be deleted. 138 | data.$set.should.have.not.own.property('total_votes'); 139 | data.$set.should.have.not.own.property('agree_votes'); 140 | data.$set.should.have.not.own.property('disagree_votes'); 141 | }); 142 | 143 | sinon.mock(UpdateProposal) 144 | .expects('find') 145 | .atLeast(1) 146 | .chain('limit', 30) 147 | .atLeast(1) 148 | .chain('sort', '-id') 149 | .atLeast(1) 150 | .resolves([ 151 | {proposer : 'xxxxxxxxxxxx', proposal_id : 0, remove : async function() {}} 152 | ]); 153 | 154 | await updateProposals(); 155 | }); 156 | 157 | it('update', async function() { 158 | sandbox.replace(Proposal, 'updateOne', async function(condition, data) { 159 | // console.log(data); 160 | // The following values must be deleted. 161 | data.$set.should.have.not.own.property('total_votes'); 162 | data.$set.should.have.not.own.property('agree_votes'); 163 | data.$set.should.have.not.own.property('disagree_votes'); 164 | }); 165 | 166 | sinon.mock(UpdateProposal) 167 | .expects('find') 168 | .atLeast(1) 169 | .chain('limit', 30) 170 | .atLeast(1) 171 | .chain('sort', '-id') 172 | .atLeast(1) 173 | .resolves([ 174 | {proposal_id : 1, remove : async function() {}} 175 | ]); 176 | 177 | await updateProposals(); 178 | }); 179 | 180 | }); 181 | }); 182 | }; 183 | -------------------------------------------------------------------------------- /backend/test/batch/update_summary/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function() { 4 | describe('update_summary', () => { 5 | const sinon = require('sinon'), 6 | SEnum = require('libs/enum'), 7 | mongo = require('models/mongo'); 8 | 9 | require('sinon-mongoose'); 10 | require('chai').should(); 11 | 12 | const updateSummary = require('libs/batch/update_summary').updateSummary; 13 | 14 | const Summary = mongo.LibSummary; 15 | const Proposal = mongo.LibProposal; 16 | const VotingInfo = mongo.LibVotingInfo; 17 | /* 18 | PROPOSAL_STATUS_PENDING : 1, 19 | PROPOSAL_STATUS_REJECTED : 2, 20 | PROPOSAL_STATUS_ON_VOTE : 3, 21 | PROPOSAL_STATUS_FINISHED_VOTING : 4, 22 | PROPOSAL_STATUS_CHECK_VOTE : 5, // check count of votes 23 | PROPOSAL_STATUS_CHECKED_VOTE : 6, // checked count of votes by platform 24 | PROPOSAL_STATUS_APPROVED : 7, // approve 25 | PROPOSAL_STATUS_COMPLETED : 8, 26 | */ 27 | const summary = { 28 | block_id : '5b7bc8ec9b53c169b2caa48c', 29 | block_num : 100, 30 | total_voters : 1000, 31 | total_proposals : 20, 32 | funded_proposals : 10, 33 | ongoing_proposals : 10, 34 | }; 35 | 36 | const statusCnts = [{ _id: 1, count: 12 }, { _id: 3, count: 5 }]; 37 | 38 | const sandbox = sinon.createSandbox(); 39 | describe('vote', () => { 40 | beforeEach(() => { 41 | sinon.mock(Summary) 42 | .expects('findOne') 43 | .atLeast(1) 44 | .atMost(10) 45 | .resolves(Object.assign({}, summary)); 46 | 47 | sinon.mock(Proposal) 48 | .expects('aggregate') 49 | .atLeast(1) 50 | .atMost(10) 51 | .resolves(Object.assign({}, statusCnts)); 52 | 53 | sinon.mock(VotingInfo) 54 | .expects('distinct') 55 | .withArgs('account') 56 | .chain('countDocuments') 57 | .atLeast(1) 58 | .atMost(10) 59 | .resolves(101); 60 | }); 61 | 62 | afterEach(() => { 63 | sinon.restore(); 64 | sandbox.restore(); 65 | }); 66 | 67 | it('update', async function() { 68 | sandbox.replace(Summary, 'updateOne', async function(condition, data) { 69 | data.$set.should.have.own.property('total_voters'); 70 | data.$set.should.have.own.property('total_proposals'); 71 | data.$set.should.have.own.property('funded_proposals'); 72 | data.$set.should.have.own.property('ongoing_proposals'); 73 | }); 74 | 75 | await updateSummary(); 76 | }); 77 | }); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /backend/test/batch/update_votings/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = exports = function() { 4 | describe('update_votings', () => { 5 | require('./check_voting')(); 6 | require('./stakeunstake')(); 7 | require('./vote')(); 8 | require('./unvote')(); 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /backend/test/batch/update_votings/stakeunstake.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sinon = require('sinon'), 4 | SEnum = require('libs/enum'), 5 | mongo = require('models/mongo'), 6 | eosApi = require('external_apis/eos_api'); 7 | 8 | require('sinon-mongoose'); 9 | require('chai').should(); 10 | 11 | const updateVotings = require('libs/batch/update_votings').updateVotings; 12 | 13 | const Proposal = mongo.LibProposal; 14 | const UpdateVoting = mongo.LibUpdateVoting; 15 | 16 | const WpsInfo = mongo.WpsInfo; 17 | const VotingInfo = mongo.LibVotingInfo; 18 | const VoterInfo = mongo.LibVoterInfo; 19 | 20 | /* 21 | PROPOSAL_STATUS_PENDING : 1, 22 | PROPOSAL_STATUS_REJECTED : 2, 23 | PROPOSAL_STATUS_ON_VOTE : 3, 24 | PROPOSAL_STATUS_FINISHED_VOTING : 4, 25 | PROPOSAL_STATUS_CHECK_VOTE : 5, // check count of votes 26 | PROPOSAL_STATUS_CHECKED_VOTE : 6, // checked count of votes by platform 27 | PROPOSAL_STATUS_APPROVED : 7, // approve 28 | PROPOSAL_STATUS_COMPLETED : 8, 29 | */ 30 | 31 | module.exports = exports = function() { 32 | const proposal = { 33 | _id : '5b7bc8ec9b53c169b2caa48c', 34 | proposer: 'xxxxxxxxxxx', 35 | id : 1, 36 | proposal_id : 1, 37 | committee: 'committeeaaa', 38 | category: 'emergency', 39 | subcategory: 1, 40 | title: 'wps project title', 41 | summary: 'wps proejct summary', 42 | project_img_url: 'http://www.google.com', 43 | description: 44 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 45 | roadmap: 46 | 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 47 | duration: 30, 48 | members: [ 'yepp4you1', 'yepp4you2', 'yepp4you3' ], 49 | funding_goal: '10.0000 EOS', 50 | total_votes: 10000000, 51 | agree_votes: 5200000, 52 | disagree_votes: 4800000, 53 | status: SEnum.PROPOSAL_STATUS_CHECK_VOTE, 54 | vote_start_time: 1534733451, 55 | fund_start_time: 0, 56 | iteration_of_funding: 0 57 | }; 58 | const chainInfo = { 59 | server_version: 'bf28f8bb', 60 | chain_id: 'a4fe43c897279a677025624555f3835b949f995f87923c97f0392dcff835bfd0', 61 | head_block_num: 208607, 62 | last_irreversible_block_num: 208271, 63 | last_irreversible_block_id: '00032d8fd0bb3ca9d0b46dec6be826d2d57b06ee569abebcfbb4ffda105624de', 64 | head_block_id: '00032edf764f42f9f30dfce850ef60ac46a1b0c14eb90dd595da457c1bc14e8c', 65 | head_block_time: '2018-08-22T09:40:17.500', 66 | head_block_producer: 'producer111h', 67 | virtual_block_cpu_limit: 200000000, 68 | virtual_block_net_limit: 1048576000, 69 | block_cpu_limit: 199900, 70 | block_net_limit: 1048576, 71 | server_version_string: 'v1.2.1-dirty' 72 | }; 73 | const stats = { EOS: 74 | { supply: '20000.0000 EOS', max_supply: '100000.0000 EOS', issuer: 'eosio' } 75 | // { supply: '1000004050.0000 EOS', max_supply: '10000000000.0000 EOS', issuer: 'eosio' } 76 | }; 77 | 78 | const wpsInfo = { 79 | watchman_account : 'committeeaab', 80 | watchman_prv : '5K6LU8aVpBq9vJsnpCvaHCcyYwzPPKXfDdyefYyAMMs3Qy42fUr', 81 | watchman_pub : 'EOS7WnhaKwHpbSidYuh2DF1qAExTRUtPEdZCaZqt75cKcixuQUtdA', 82 | total_voting_percent : 5, 83 | agree_percent : 10 84 | }; 85 | 86 | const votingInfo = { 87 | _id: '5b7bc8ef9b53c169b2caa57e', 88 | proposal_id: 1, 89 | account: 'useraaaaaabg', 90 | staked: 10000, 91 | is_agree: true, 92 | __v: 0 93 | }; 94 | 95 | const voter = { 96 | owner: 'useraaaaaabg', 97 | proxy: '', 98 | producers: [ 'producer1111', 'producer1112', 'producer1113', 'producer111j', 'producer111k', 'producer111l', 'producer111m', 'producer111n', 'producer111o', 'producer111p', 'producer111q', 99 | 'producer111r', 'producer111s', 'producer111t', 'producer111u', 'producer111v', 'producer111w', 'producer111x', 'producer111y', 'producer111z' 100 | ], 101 | staked: 30000, 102 | last_vote_weight: 0, 103 | proxied_vote_weight: 0.00000000000000000, 104 | }; 105 | 106 | 107 | const sandbox = sinon.createSandbox(); 108 | describe('stakeunstake', () => { 109 | before(() => { 110 | sinon.mock(Proposal) 111 | .expects('findOne') 112 | .atLeast(1) 113 | .atMost(10) 114 | .withArgs({proposal_id : 1}) 115 | .resolves(Object.assign({}, proposal, {save : async function() {}})); 116 | 117 | sinon.mock(WpsInfo) 118 | .expects('findOne') 119 | .atLeast(1) 120 | .atMost(10) 121 | .resolves(wpsInfo); 122 | 123 | sinon.mock(VotingInfo) 124 | .expects('findOne') 125 | .atLeast(1) 126 | .atMost(10) 127 | .resolves(Object.assign({}, votingInfo, {save : async function() {}})); 128 | 129 | sinon.mock(VoterInfo) 130 | .expects('create') 131 | .atLeast(1) 132 | .resolves({}); 133 | 134 | sandbox.replace(eosApi, 'getInfo', async function() { 135 | return chainInfo; 136 | }); 137 | 138 | sandbox.replace(eosApi, 'getCurrencyStats', async function() { 139 | return stats; 140 | }); 141 | 142 | sandbox.replace(eosApi, 'getVoterInfo', async function(_voter) { 143 | return voter; 144 | }); 145 | 146 | sandbox.replace(eosApi, 'getProposalByOwner', async function(proposer) { 147 | return Object.assign({}, proposal); 148 | }); 149 | 150 | sandbox.replace(eosApi, 'getProposalById', async function(proposalId) { 151 | return Object.assign({}, proposal); 152 | }); 153 | 154 | sandbox.replace(eosApi, 'getRejectedProposalById', async function(proposalId) { 155 | return Object.assign({}, proposal); 156 | }); 157 | 158 | sandbox.replace(eosApi, 'getFinishedProposalById', async function(proposalId) { 159 | return Object.assign({}, proposal); 160 | }); 161 | 162 | sandbox.replace(eosApi, 'checkExpire', async function(params, key, authorization, endpoint) { 163 | return {}; 164 | }); 165 | }); 166 | 167 | afterEach(() => { 168 | sinon.restore(); 169 | sandbox.restore(); 170 | }); 171 | 172 | it('stake and unstake', async function() { 173 | sinon.mock(UpdateVoting) 174 | .expects('find') 175 | .atLeast(1) 176 | .chain('limit', 30) 177 | .atLeast(1) 178 | .chain('sort', '-id') 179 | .atLeast(1) 180 | .resolves([ 181 | {proposal_id : 1, account : 'useraaaaaabg', action : SEnum.VOTE_ACTION_STAKE, remove : async function() {}, save : async function() {}}, 182 | {proposal_id : 1, account : 'useraaaaaabg', action : SEnum.VOTE_ACTION_UNSTAKE, remove : async function() {}, save : async function() {}} 183 | ]); 184 | 185 | await updateVotings(); 186 | 187 | const postProposal = await Proposal.findOne({proposal_id : 1}); 188 | 189 | (proposal.total_votes - postProposal.total_votes).should.to.equal(votingInfo.staked - voter.staked); 190 | if (votingInfo.is_agree === true) { 191 | (proposal.agree_votes - postProposal.agree_votes).should.to.equal(votingInfo.staked - voter.staked); 192 | } else { 193 | (proposal.disagree_votes - postProposal.disagree_votes).should.to.equal(votingInfo.staked - voter.staked); 194 | } 195 | }); 196 | }); 197 | }; 198 | 199 | -------------------------------------------------------------------------------- /backend/test/data/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | path = require('path'); 5 | 6 | var dummy = {}; 7 | 8 | fs.readdirSync(__dirname).filter(function(file) { 9 | return (file.indexOf('.') !== 0) && (file !== 'index.js'); 10 | }) 11 | .forEach(function(file) { 12 | dummy = Object.assign({}, dummy, require(path.join(__dirname, file))); 13 | }); 14 | 15 | module.exports = dummy; 16 | -------------------------------------------------------------------------------- /backend/test/external_apis/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('chai').should(); 4 | const eosApi = require('external_apis/eos_api'); 5 | 6 | describe('[external_apis]', function() { 7 | it('getInfo', async function() { 8 | return eosApi.getInfo() 9 | .then(function(result) { 10 | console.log(result); 11 | }); 12 | }); 13 | 14 | it('getBlock', async function() { 15 | return eosApi.getBlock(10) 16 | .then(function(result) { 17 | console.log(result); 18 | }); 19 | }); 20 | 21 | it('getVoterInfo', async function() { 22 | return eosApi.getVoterInfo('useraaaaaabg') 23 | .then(function(result) { 24 | console.log(result); 25 | }); 26 | }); 27 | 28 | it('getProposalById', async function() { 29 | return eosApi.getProposalById(1) 30 | .then(function(result) { 31 | console.log(result); 32 | }); 33 | }); 34 | 35 | it('getProposalById', async function() { 36 | return eosApi.getProposalById(2) 37 | .then(function(result) { 38 | console.log(result); 39 | }); 40 | }); 41 | 42 | it('getProposalById', async function() { 43 | return eosApi.getProposalById(3) 44 | .then(function(result) { 45 | console.log(result); 46 | }); 47 | }); 48 | 49 | it('getProposalById', async function() { 50 | return eosApi.getProposalById(4) 51 | .then(function(result) { 52 | console.log(result); 53 | }); 54 | }); 55 | 56 | it.skip('getProposalByOwner', async function() { 57 | return eosApi.getProposalByOwner('proposeraaaa') 58 | .then(function(result) { 59 | console.log(result); 60 | }); 61 | }); 62 | 63 | it('getRejectedProposalById', async function() { 64 | return eosApi.getRejectedProposalById(10) 65 | .then(function(result) { 66 | console.log(result); 67 | }); 68 | }); 69 | 70 | it('getFinishedProposalById', async function() { 71 | return eosApi.getFinishedProposalById(11) 72 | .then(function(result) { 73 | console.log(result); 74 | }); 75 | }); 76 | 77 | it('getCurrencyStats', async function() { 78 | return eosApi.getCurrencyStats() 79 | .then(function(result) { 80 | console.log(result); 81 | }); 82 | }); 83 | 84 | }); 85 | 86 | -------------------------------------------------------------------------------- /backend/test/libs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const data = require('test/data'); 4 | const SUtil = require('libs/util'); 5 | 6 | require('chai').should(); 7 | 8 | describe('[libs]', function() { 9 | 10 | }); 11 | 12 | -------------------------------------------------------------------------------- /backend/test/routes/api/committee.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | chai = require('chai'), 5 | app = require('app'); 6 | 7 | let request = Promise.promisifyAll(require('supertest')); 8 | chai.should(); 9 | 10 | /* 11 | router.get('/1/committees', getCommittees); 12 | */ 13 | 14 | module.exports = exports = function() { 15 | it('get /api/1/committees should receive committees', function() { 16 | return request(app) 17 | .get('/api/1/committees') 18 | .endAsync().then(function(res) { 19 | res.body.should.not.have.property('error'); 20 | res.body.should.have.property('committees'); 21 | console.log(res.body.committees); 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /backend/test/routes/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('[routes_api]', function() { 4 | require('./committee')(); 5 | require('./proposer')(); 6 | require('./proposal')(); 7 | require('./user')(); 8 | require('./stats')(); 9 | }); 10 | 11 | -------------------------------------------------------------------------------- /backend/test/routes/api/proposal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | chai = require('chai'), 5 | app = require('app'), 6 | data = require('test/data'); 7 | 8 | let request = Promise.promisifyAll(require('supertest')); 9 | chai.should(); 10 | 11 | /* 12 | router.get('/1/committees/:committee/proposals/orders/:order', getProposals); 13 | router.get('/1/committees/:committee/proposals/:proposal_id', getProposal); 14 | */ 15 | 16 | module.exports = exports = function() { 17 | const committees = data.committees; 18 | const committee = committees[0]; 19 | it('get /1/committees/:committee/proposals/orders/hot should receive proposals', function() { 20 | return request(app) 21 | .get(`/api/1/committees/${committee.committeeman}/proposals/orders/hot`) 22 | .endAsync().then(function(res) { 23 | res.body.should.not.have.property('error'); 24 | res.body.should.have.property('proposals'); 25 | console.log(res.body.proposals); 26 | }); 27 | }); 28 | 29 | it('get /1/committees/:committee/proposals/orders/latest should receive proposals', function() { 30 | return request(app) 31 | .get(`/api/1/committees/${committee.committeeman}/proposals/orders/latest`) 32 | .endAsync().then(function(res) { 33 | res.body.should.not.have.property('error'); 34 | res.body.should.have.property('proposals'); 35 | console.log(res.body.proposals); 36 | }); 37 | }); 38 | 39 | it('get /1/committees/:committee/proposals/:proposal_id should receive proposal', function() { 40 | return request(app) 41 | .get(`/api/1/committees/${committee.committeeman}/proposals/1`) 42 | .endAsync().then(function(res) { 43 | res.body.should.not.have.property('error'); 44 | res.body.should.have.property('proposal'); 45 | console.log(res.body.proposal); 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /backend/test/routes/api/proposer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | chai = require('chai'), 5 | app = require('app'), 6 | data = require('test/data'); 7 | 8 | let request = Promise.promisifyAll(require('supertest')); 9 | chai.should(); 10 | 11 | /* 12 | router.get('/1/proposers/:proposer/proposals', getProposals); 13 | */ 14 | 15 | module.exports = exports = function() { 16 | const proposers = data.proposers; 17 | const proposer = proposers[0]; 18 | it('get /1/proposers/:proposer/proposals should receive proposals', function() { 19 | return request(app) 20 | .get(`/api/1/proposers/${proposer.account}/proposals`) 21 | .endAsync().then(function(res) { 22 | res.body.should.not.have.property('error'); 23 | res.body.should.have.property('proposals'); 24 | console.log(res.body.proposals); 25 | }); 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /backend/test/routes/api/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | chai = require('chai'), 5 | app = require('app'); 6 | 7 | let request = Promise.promisifyAll(require('supertest')); 8 | chai.should(); 9 | 10 | /* 11 | router.get('/1/summary', getSummary); 12 | */ 13 | 14 | module.exports = exports = function() { 15 | it('get /api/1/summary should receive summary', function() { 16 | return request(app) 17 | .get('/api/1/summary') 18 | .endAsync().then(function(res) { 19 | res.body.should.not.have.property('error'); 20 | res.body.should.have.property('summary'); 21 | console.log(res.body.summary); 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /backend/test/routes/api/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Promise = require('bluebird'), 4 | chai = require('chai'), 5 | app = require('app'), 6 | data = require('test/data'); 7 | 8 | let request = Promise.promisifyAll(require('supertest')); 9 | chai.should(); 10 | 11 | /* 12 | router.post('/1/users', createUser); 13 | router.put('/1/users/:user', updateUser); 14 | router.get('/1/users/:user', getUser); 15 | */ 16 | 17 | module.exports = exports = function() { 18 | const user = data.user; 19 | let userRes = null; 20 | it('post /1/users should receive user', function() { 21 | return request(app) 22 | .post('/api/1/users') 23 | .send(user) 24 | .endAsync().then(function(res) { 25 | res.body.should.not.have.property('error'); 26 | res.body.should.have.property('user'); 27 | userRes = res.body.user; 28 | }); 29 | }); 30 | 31 | it('put /1/users/:eos_account should receive user', function() { 32 | return request(app) 33 | .put(`/api/1/users/${userRes._id}`) 34 | .send(user) 35 | .endAsync().then(function(res) { 36 | res.body.should.not.have.property('error'); 37 | res.body.should.have.property('user'); 38 | userRes = res.body.user; 39 | }); 40 | }); 41 | 42 | it('get /1/users/:eos_account should receive user', function() { 43 | return request(app) 44 | .get(`/api/1/users/${userRes.eos_account}`) 45 | .endAsync().then(function(res) { 46 | res.body.should.not.have.property('error'); 47 | res.body.should.have.property('user'); 48 | }); 49 | }); 50 | 51 | it('delete /1/users/:eos_account should receive user', function() { 52 | return request(app) 53 | .delete(`/api/1/users/${userRes.eos_account}`) 54 | .endAsync().then(function(res) { 55 | res.body.should.not.have.property('error'); 56 | res.body.should.have.property('result'); 57 | }); 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /backend/tools/create_wps_info.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Promise = require('bluebird'), 4 | _ = require('lodash'), 5 | mongo = require('models/mongo'); 6 | 7 | console.log(`ENV : ${process.env.NODE_ENV}`); 8 | 9 | if (process.env.NODE_ENV === 'production') { 10 | console.log('NOT SUPPORT IN PRODUCTION'); 11 | return; 12 | } 13 | 14 | const WpsInfo = mongo.WpsInfo; 15 | const Summary = mongo.LibSummary; 16 | 17 | // sample 18 | const wpsInfo = { 19 | watchman_account : 'committeeaab', 20 | watchman_prv : '5K6LU8aVpBq9vJsnpCvaHCcyYwzPPKXfDdyefYyAMMs3Qy42fUr', 21 | watchman_pub : 'EOS7WnhaKwHpbSidYuh2DF1qAExTRUtPEdZCaZqt75cKcixuQUtdA', 22 | total_voting_percent : 5, 23 | agree_percent : 10 24 | }; 25 | 26 | async function upsertWps() { 27 | const wps = await WpsInfo.findOne(); 28 | if (_.isEmpty(wps)) { 29 | return WpsInfo.create(wpsInfo); 30 | } else { 31 | return WpsInfo.updateOne({_id : wps._id}, wpsInfo); 32 | } 33 | } 34 | 35 | async function createSummary() { 36 | const summary = await Summary.findOne(); 37 | if (_.isEmpty(summary)) { 38 | return Summary.create({block_num : 100}); 39 | } else { 40 | // return Summary.updateOne({block_num : 0}); 41 | } 42 | } 43 | 44 | Promise.resolve(upsertWps()).delay(100) 45 | .then(() => { 46 | return createSummary(); 47 | }) 48 | .then(() => { 49 | console.log('complete~~'); 50 | }) 51 | .delay(1000); 52 | 53 | 54 | -------------------------------------------------------------------------------- /contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # vscode 35 | .vscode 36 | 37 | # build 38 | build 39 | -------------------------------------------------------------------------------- /contracts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(eosio_wps_contracts VERSION 1.2.0) 3 | 4 | set(EOSIO_DEPENDENCY "1.2") 5 | set(EOSIO_CDT_DEPENDENCY "1.2") 6 | 7 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 8 | set(TEST_BUILD_TYPE "Debug") 9 | set(CMAKE_BUILD_TYPE "Release") 10 | else() 11 | set(TEST_BUILD_TYPE ${CMAKE_BUILD_TYPE}) 12 | endif() 13 | 14 | if(EOSIO_ROOT STREQUAL "" OR NOT EOSIO_ROOT) 15 | set(EOSIO_ROOT "/usr/local/eosio") 16 | endif() 17 | 18 | if(EOSIO_CDT_ROOT STREQUAL "" OR NOT EOSIO__ROOT) 19 | set(EOSIO_CDT_ROOT "/usr/local/eosio.cdt") 20 | endif() 21 | 22 | list(APPEND CMAKE_MODULE_PATH ${EOSIO_CDT_ROOT}/lib/cmake) 23 | include(EosioWasmToolchain) 24 | 25 | ### Check the version of eosio.cdt 26 | string(FIND "${EOSIO_CDT_VERSION}" "${EOSIO_CDT_DEPENDENCY}" output) 27 | 28 | if (NOT "${output}" EQUAL 0) 29 | message(FATAL_ERROR "Incorrect EOSIO.CDT version, please use version ${EOSIO_CDT_DEPENDENCY}.x") 30 | endif() 31 | 32 | include_directories(AFTER ${BOOST_ROOT}/include) 33 | add_subdirectory(eosio.wps) 34 | 35 | if (APPLE) 36 | set(OPENSSL_ROOT "/usr/local/opt/openssl") 37 | elseif (UNIX) 38 | set(OPENSSL_ROOT "/usr/include/openssl") 39 | endif() 40 | set(SECP256K1_ROOT "/usr/local") 41 | 42 | include(UnitTestsExternalProject.txt) 43 | -------------------------------------------------------------------------------- /contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/UnitTestsExternalProject.txt: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | find_package(Git REQUIRED) 3 | include(GNUInstallDirs) 4 | 5 | ExternalProject_Add( 6 | contracts_unit_tests 7 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DEOSIO_ROOT=${EOSIO_ROOT} -DEOSIO_DEPENDENCY=${EOSIO_DEPENDENCY} 8 | 9 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/tests 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/tests 11 | BUILD_ALWAYS 1 12 | TEST_COMMAND "" 13 | INSTALL_COMMAND "" 14 | ) 15 | -------------------------------------------------------------------------------- /contracts/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | printf "\t=========== Building eosio.contracts ===========\n\n" 4 | 5 | RED='\033[0;31m' 6 | NC='\033[0m' 7 | 8 | CORES=`getconf _NPROCESSORS_ONLN` 9 | mkdir -p build 10 | pushd build &> /dev/null 11 | cmake ../ 12 | make -j${CORES} 13 | popd &> /dev/null -------------------------------------------------------------------------------- /contracts/eosio.wps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #add_library(eosio.wps.lib 2 | # ${CMAKE_CURRENT_SOURCE_DIR}/src/proposal.cpp 3 | # ${CMAKE_CURRENT_SOURCE_DIR}/src/proposer.cpp) 4 | 5 | #target_include_directories(eosio.wps.lib 6 | # PUBLIC 7 | # ${CMAKE_CURRENT_SOURCE_DIR}/include) 8 | 9 | add_executable(eosio.wps.wasm ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.wps.cpp) 10 | target_include_directories(eosio.wps.wasm 11 | PUBLIC 12 | ${CMAKE_CURRENT_SOURCE_DIR}/include) 13 | 14 | #target_link_libraries(eosio.wps.wasm eosio.wps.lib) 15 | 16 | set_target_properties(eosio.wps.wasm 17 | PROPERTIES 18 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/eosio.wps") 19 | 20 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/abi/eosio.wps.abi" "${CMAKE_CURRENT_SOURCE_DIR}/bin/eosio.wps/eosio.wps.abi" COPYONLY) 21 | 22 | include_directories(${EOSIO_ROOT}/include) 23 | 24 | # install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${WASM_ROOT}/eosio.wasmsdk/include) 25 | -------------------------------------------------------------------------------- /contracts/eosio.wps/README.md: -------------------------------------------------------------------------------- 1 | eosio.wps 2 | ---------- 3 | 4 | This contract enables users to stake tokens, and then configure and vote on producers and worker proposals. 5 | 6 | Users can also proxy their voting influence to other users. 7 | 8 | The state of this contract is read to determine the 21 active block producers. 9 | 10 | Actions: 11 | The naming convention is codeaccount::actionname followed by a list of paramters. -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-acceptprop-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ acceptprop }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ acceptprop }}` action is to pass a proposal to go on vote after it has been reviewed according to the standard proposal checklist. 6 | 7 | I, `{{ reviewer }}`, agree to serve as an elected Representative to the EOS Worker Proposal System committee that I am registered with. 8 | 9 | If I, `{{ reviewer }}`, am unable to perform obligations under this contract I will resign my position as a Committee Representative. 10 | 11 | I, `{{ reviewer }}`, hereby agree that I will work toward the goals outlined in the Committee Charter, and pledge to follow any additional rules specified on the Committee Charter. 12 | 13 | I, `{{ reviewer }}`, commit to evaluate and review proposals on a first in first offered and best-effort basis. 14 | 15 | I, `{{ reviewer}}`, pledge to never use any information acquired during the course of my duties for personal gain or to further a personal agenda. 16 | 17 | I, `{{ reviewer }}`, attest that I will take any decisions related to my duty as a Committee Representative independently from any business interests I may have. If I have a conflict of interest with regards to a proposal, I will recuse myself from any related decisions. 18 | 19 | I, `{{ reviewer }}`, agree to never accept anything of value in return for consideration or decision on a proposal submitted through the EOS Worker Proposal System. 20 | 21 | I, `{{ reviewer }}`, hereby agree to cooperate with other Committee Representatives to carry out our respective and mutual obligations under this agreement, including but not limited to evaluating, accepting or rejecting proposals based on their merit and potential value to the EOS community. 22 | 23 | I hereby acknowledge that other Committee Representatives may vote to disqualify `{{ reviewer }}` through an unanimous vote in the event that I, `{{ reviewer }}`, have been impeached or am unable to be reached, according to criteria agreed to among Committee Representatives. 24 | 25 | I, `{{ reviewer }}`, hereby agree that I will, in collaboration with other Committee Representatives, review and determine if a project is legitimate and beneficial for the community based on the public proposal review checklist. 26 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-approve-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ approve }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ approve }}` action is to change the status of a proposal from `CHECKED_VOTES` to `APPROVED` in order to begin its funding schedule. 6 | 7 | I, {{reviewer}}, agree to serve as an elected Representative to the EOS Worker Proposal System {{committee}}. 8 | 9 | If I, {{reviewer}}, am unable to perform obligations under this contract I will resign my position as a Committee Representative. 10 | 11 | I, {{reviewer}}, hereby agree that I will work toward the goals outlined in the Committee Charter, and pledge to follow any additional rules specified on the Committee Charter. 12 | 13 | I, {{reviewer}}, commit to evaluate and review proposals on a first in first offered and best-effort basis. 14 | 15 | I, {{reviewer}}, pledge to never use any information acquired during the course of my duties for personal gain or to further a personal agenda. 16 | 17 | I, {{reviewer}}, attest that I will take any decisions related to my duty as a Committee Representative independently from any business interests I may have. If I have a conflict of interest with regards to a proposal, I will recuse myself from any related decisions. 18 | 19 | I, {{reviewer}}, agree to never accept anything of value in return for consideration or decision on a proposal submitted through the EOS Worker Proposal System. 20 | 21 | I, {{reviewer}}, hereby agree to cooperate with other Committee Representatives to carry out our respective and mutual obligations under this agreement, including but not limited to evaluating, accepting or rejecting proposals based on their merit and potential value to the EOS community. 22 | 23 | I hereby acknowledge that other Committee Representatives may vote to disqualify {{reviewer}} through an unanimous vote in the event that I, {{reviewer}}, have been impeached or am unable to be reached, according to criteria agreed to among Committee Representatives. 24 | 25 | I, {{reviewer}}, hereby agree that I will, in collaboration with other Committee Representatives, review and determine if a project is legitimate and beneficial for the community based on the public proposal review checklist. 26 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-checkvote-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ checkvote }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ checkvote }}` action is to request the database to verify that the proposal has passed the vote threshold defined in the `{{ wpsglobal }}` table. 6 | 7 | I, `{{ reviewer }}`, agree to serve as an elected Representative to the EOS Worker Proposal System committee that I am registered with. 8 | 9 | If I, `{{ reviewer }}`, am unable to perform obligations under this contract I will resign my position as a Committee Representative. 10 | 11 | I, `{{ reviewer }}`, hereby agree that I will work toward the goals outlined in the Committee Charter, and pledge to follow any additional rules specified on the Committee Charter. 12 | 13 | I, `{{ reviewer }}`, commit to evaluate and review proposals on a first in first offered and best-effort basis. 14 | 15 | I, `{{ reviewer}}`, pledge to never use any information acquired during the course of my duties for personal gain or to further a personal agenda. 16 | 17 | I, `{{ reviewer }}`, attest that I will take any decisions related to my duty as a Committee Representative independently from any business interests I may have. If I have a conflict of interest with regards to a proposal, I will recuse myself from any related decisions. 18 | 19 | I, `{{ reviewer }}`, agree to never accept anything of value in return for consideration or decision on a proposal submitted through the EOS Worker Proposal System. 20 | 21 | I, `{{ reviewer }}`, hereby agree to cooperate with other Committee Representatives to carry out our respective and mutual obligations under this agreement, including but not limited to evaluating, accepting or rejecting proposals based on their merit and potential value to the EOS community. 22 | 23 | I hereby acknowledge that other Committee Representatives may vote to disqualify `{{ reviewer }}` through an unanimous vote in the event that I, `{{ reviewer }}`, have been impeached or am unable to be reached, according to criteria agreed to among Committee Representatives. 24 | 25 | I, `{{ reviewer }}`, hereby agree that I will, in collaboration with other Committee Representatives, review and determine if a project is legitimate and beneficial for the community based on the public proposal review checklist. 26 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-editproposer-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ editproposer }}` 2 | 3 | I, `{{ proposer }}`, hereby attest to only submit proposals that I personally take part in as a team member. 4 | 5 | I, `{{ proposer }}`, agree to only submit proposals that are aimed to benefit the EOS ecosystem. 6 | 7 | I, `{{ proposer }}`, promise to provide correct and truthful information about the proposals I will be submitting to the EOS Worker Proposal System. If I acquire knowledge of any incorrect information that I may have submitted with a proposal, or if any information I have submitted changes, I will update the said proposal as soon as I am capable of doing so. 8 | 9 | I, `{{ proposer`}}`, agree and promise to distribute any funding that my proposal may receive to the relevant parties, as outlined in my proposal submission. I attest that my proposal includes all relevant details of existing benefits, and declares any and all conflicts of interest. 10 | 11 | I, `{{ proposer }}`, hereby attest that the information I provide about myself is accurate. 12 | 13 | I, `{{ proposer }}`, agree that any proposal that I submit in the future meets the criteria of the standard proposal checklist to the best of my knowledge. 14 | 15 | I, `{{ proposer }}`, agree that my proposals may be rejected if they do not meet the publicly available proposal checklist. 16 | 17 | I, `{{ proposer }}`, agree that my proposals may be rejected if I do not meet any or some significant deliverables/milestones outlined in the proposals’ roadmaps. 18 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-editreviewer-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ editreviewer }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ editreviewer }}` action is to edit the information of an already registered Committee Representative. 6 | 7 | We, the `{{ committee }}`, agree to follow any additional rules and/or guidelines for the addition, removal, or any change of a reviewer to this `{{ committee }}` specified on the Committee Charter associated with the `{{`committee }}`. 8 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-regcommittee-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ regcommittee }}` 2 | 3 | ## Description 4 | 5 | The intent of the `regcommittee` action is to register an account as an official committee in the EOS Commons Fund system. 6 | 7 | The {{committee}} account is controlled by a multisig signing key that can have different privileges depending on the threshold of signers reached. The exact signing privilege rules are outlined in the {{committee}} charter and implemented with the registration of the committee. 8 | 9 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-regproposer-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ regproposer }}` 2 | 3 | ## Description 4 | 5 | The intent of the `regproposer` action is to register an account as the responsible party for submitting a proposal to the EOS Commons Fund. 6 | 7 | I, {{proposer}}, hereby attest to only submit proposals that I personally take part in as a team member. 8 | 9 | I, {{proposer}}, agree to only submit proposals that are aimed to benefit the EOS ecosystem. 10 | 11 | I, {{proposer}}, promise to provide correct and truthful information about the proposals I will be submitting to the EOS Commons Fund. If I acquire knowledge of any incorrect information that I may have submitted with a proposal, or if any information I have submitted changes, I will update said proposal within a reasonable timeframe. 12 | 13 | I, {{proposer}}, agree and promise to distribute any funding that my proposal may receive to the relevant parties, as outlined in my proposal submission. I attest that my proposal includes all relevant details of how I will personally benefit, and declares any and all conflicts of interest. 14 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-regreviewer-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ regreviewer }}` 2 | 3 | ## Description 4 | 5 | The intent of the `regreviewer` action is to register an account as a Committee Representative. 6 | 7 | I, {{reviewer}}, hereby agree to serve as an elected Representative to the EOS Commons Fund {{committee}}. 8 | 9 | If I, {{reviewer}}, am unable to perform obligations under this contract I will resign my position as a Committee Representative. 10 | 11 | I, {{reviewer}}, pledge to never use any information acquired during the course of my duties for personal gain or to further a personal agenda. 12 | 13 | I, {{reviewer}}, attest that I will take any decisions related to my duty as a Committee Representative independently from any business interests I may have. If I have a conflict of interest with regards to a proposal, I will recuse myself from any related decisions. 14 | 15 | I, {{reviewer}}, agree to never accept anything of value in return for consideration or decision on a proposal submitted through the EOS Commons Fund system. 16 | 17 | I, {{reviewer}}, hereby agree to cooperate with other Committee Representatives to carry out our respective and mutual obligations under this agreement, including but not limited to evaluating, accepting or rejecting proposals based on their merit and potential value to the EOS community. 18 | 19 | I, {{reviewer}}, commit to evaluate and review proposals on a first in first offered and best-effort basis. 20 | 21 | I hereby acknowledge that other Committee Representatives may vote to disqualify {{reviewer}} through an unanimous vote in the event that I, {{reviewer}}, have been impeached or am unable to be reached, according to criteria agreed to among Committee Representatives. 22 | 23 | I, {{reviewer}}, hereby agree that I will, in collaboration with other Committee Representatives, review and determine if a project is deemed as qualified as per the rules of the Committee. 24 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-rejectfund-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ rejectfund }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ rejectfund }}` action is to reject a proposal being funded if and only if there is overwhelming evidence that the proposer has violated an item on the proposer’s Ricardian Contracts. 6 | 7 | We, the `{{ committee }}`, pledge that there is overwhelming evidence that the proposer of the proposal of the given `{{ proposal_id }}` has failed to meet one or more items that the proposer agreed to in the proposer’s Ricardian Contracts. 8 | 9 | We, the `{{ committee }}`, agree to follow any additional rules and/or guidelines for the rejection of funding to the proposer of the proposal of the given `{{ proposal_id }}`, specified on the Committee Charter associated with the `{{ committee }}`. 10 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-rejectprop-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ rejectprop }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ rejectprop }}` action is to reject a non-compliant proposal after it has been reviewed according to the standard proposal checklist. 6 | 7 | I, `{{ reviewer }}`, agree to serve as an elected Representative to the EOS Worker Proposal System committee that I am registered with. 8 | 9 | If I, `{{ reviewer }}`, am unable to perform obligations under this contract I will resign my position as a Committee Representative. 10 | 11 | I, `{{ reviewer }}`, hereby agree that I will work toward the goals outlined in the Committee Charter, and pledge to follow any additional rules specified on the Committee Charter. 12 | 13 | I, `{{ reviewer }}`, commit to evaluate and review proposals on a first in first offered and best-effort basis. 14 | 15 | I, `{{ reviewer}}`, pledge to never use any information acquired during the course of my duties for personal gain or to further a personal agenda. 16 | 17 | I, `{{ reviewer }}`, attest that I will take any decisions related to my duty as a Committee Representative independently from any business interests I may have. If I have a conflict of interest with regards to a proposal, I will recuse myself from any related decisions. 18 | 19 | I, `{{ reviewer }}`, agree to never accept anything of value in return for consideration or decision on a proposal submitted through the EOS Worker Proposal System. 20 | 21 | I, `{{ reviewer }}`, hereby agree to cooperate with other Committee Representatives to carry out our respective and mutual obligations under this agreement, including but not limited to evaluating, accepting or rejecting proposals based on their merit and potential value to the EOS community. 22 | 23 | I hereby acknowledge that other Committee Representatives may vote to disqualify `{{ reviewer }}` through an unanimous vote in the event that I, `{{ reviewer }}`, have been impeached or am unable to be reached, according to criteria agreed to among Committee Representatives. 24 | 25 | I, `{{ reviewer }}`, hereby agree that I will, in collaboration with other Committee Representatives, review and determine if a project is legitimate and beneficial for the community based on the public proposal review checklist. 26 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-rmvreviewer-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ rmvreviewer }}` 2 | 3 | ## Description 4 | 5 | The intent of the `{{ rmvreviewer }}` action is to remove an account as a Committee Representative. 6 | 7 | We, the `{{ committee }}`, agree to follow any additional rules and/or guidelines for the addition, removal, or any change of a reviewer to this `{{ committee }}` specified on the Committee Charter associated with the `{{`committee }}`. 8 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-unvote-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ unvote }}` 2 | 3 | ## Description 4 | 5 | The intent of the {{unvote}} action is to remove an account’s vote toward a `proposal` according to the rules of the EOS Commons Fund system. 6 | 7 | I, {{voter}}, wish to remove my vote toward {{proposal}} as per the rules of the EOS Commons Fund system. 8 | 9 | I, {{ voter }}, agree that I never accepted anything of value in return for the {{ unvote }} action toward the {{ proposal_id }}. 10 | -------------------------------------------------------------------------------- /contracts/eosio.wps/abi/eosio.wps-vote-rc.md: -------------------------------------------------------------------------------- 1 | # Action - `{{ vote }}` 2 | 3 | ## Description 4 | 5 | The intent of the {{vote}} action is to vote toward a `proposal` according to the rules of the EOS Commons Fund system. 6 | 7 | I, {{voter}}, wish to vote {{vote_param}} toward {{proposal}} as per the rules of the EOS Commons Fund system. I recognize that the weight of my vote is equal to the proportion of my staked tokens to the staked tokens owned by all account holders who used the {{vote}} action in relation to this proposal. 8 | 9 | I, {{ voter }}, agree that I never accepted anything of value in return for the {{ vote }} action toward the {{ proposal }}. 10 | -------------------------------------------------------------------------------- /contracts/eosio.wps/bin/eosio.wps/.gitignore: -------------------------------------------------------------------------------- 1 | eosio.wps.abi 2 | eosio.wps.wasm -------------------------------------------------------------------------------- /contracts/eosio.wps/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | CONTRACT_NAME="eosio.wps" 4 | 5 | mkdir -p bin/${CONTRACT_NAME} 6 | ### BUILD THE CONTRACT 7 | EOSCLANG="${PREFIX}/wasm/bin/clang++ -I${INSTALL_PREFIX}/include/libc++/upstream/include -I${INSTALL_PREFIX}/include/musl/upstream/include -I${INSTALL_PREFIX}/include -I./include -I../eosio.token/include -I${BOOST}" 8 | LINK="${PREFIX}/wasm/bin/llvm-link -only-needed " 9 | LLC="${PREFIX}/wasm/bin/llc -thread-model=single --asm-verbose=false" 10 | S2W="${INSTALL_PREFIX}/bin/eosio-s2wasm " 11 | W2W="${INSTALL_PREFIX}/bin/eosio-wast2wasm " 12 | 13 | ${EOSCLANG} -Iinclude -c -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc -DBOOST_DISABLE_ASSERTS -DBOOST_EXCEPTION_DISABLE -nostdlib -nostdlibinc -ffreestanding -nostdlib -fno-threadsafe-statics -fno-rtti -fno-exceptions -o ${CONTRACT_NAME}.bc src/${CONTRACT_NAME}.cpp 14 | ${LINK} -o linked.bc ${CONTRACT_NAME}.bc ${INSTALL_PREFIX}/usr/share/eosio/contractsdk/lib/eosiolib.bc ${INSTALL_PREFIX}/usr/share/eosio/contractsdk/lib/libc++.bc ${INSTALL_PREFIX}/usr/share/eosio/contractsdk/lib/libc.bc 15 | ${LLC} -o ${CONTRACT_NAME}.s linked.bc 16 | ${S2W} -o ${CONTRACT_NAME}.wast -s 16384 ${CONTRACT_NAME}.s 17 | ${W2W} ${CONTRACT_NAME}.wast bin/${CONTRACT_NAME}/${CONTRACT_NAME}.wasm -n 18 | cp abi/${CONTRACT_NAME}.abi bin/${CONTRACT_NAME}/${CONTRACT_NAME}.abi 19 | cp ${CONTRACT_NAME}.wast bin/${CONTRACT_NAME}/${CONTRACT_NAME}.wast 20 | 21 | rm ${CONTRACT_NAME}.bc linked.bc ${CONTRACT_NAME}.wast ${CONTRACT_NAME}.s 22 | -------------------------------------------------------------------------------- /contracts/eosio.wps/include/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOS-BP-Developers/wps-backend/053fb79b86d9909fa160b84a4ce7f234a4932fed/contracts/eosio.wps/include/.DS_Store -------------------------------------------------------------------------------- /contracts/eosio.wps/include/eosio.wps/committee.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | //#include 8 | 9 | namespace eosiowps { 10 | using std::string; 11 | using std::vector; 12 | 13 | //@abi table 14 | struct committee { 15 | account_name committeeman; 16 | string category; 17 | bool is_oversight; 18 | uint64_t primary_key() const { return committeeman; } 19 | EOSLIB_SERIALIZE( committee, (committeeman)(category)(is_oversight) ); 20 | }; 21 | 22 | typedef eosio::multi_index< N(committees), committee> committee_table; 23 | } // eosiowps -------------------------------------------------------------------------------- /contracts/eosio.wps/include/eosio.wps/eosio.wps.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* to generate abi, comment out*/ 10 | #include "proposal.hpp" 11 | #include "proposer.hpp" 12 | #include "reviewer.hpp" 13 | #include "committee.hpp" 14 | 15 | #include 16 | #include 17 | 18 | namespace eosiowps { 19 | using std::string; 20 | using std::vector; 21 | using std::set; 22 | 23 | using eosio::asset; 24 | using eosio::indexed_by; 25 | using eosio::const_mem_fun; 26 | 27 | const uint32_t seconds_per_day = 60 * 60 * 24; 28 | 29 | //@abi table 30 | struct voting_info { 31 | uint64_t proposal_id; 32 | vector agrees; 33 | vector disagrees; 34 | uint64_t primary_key() const { return proposal_id; } 35 | EOSLIB_SERIALIZE( voting_info, (proposal_id)(agrees)(disagrees) ) 36 | }; 37 | 38 | //@abi table 39 | struct wps_env { 40 | uint64_t proposal_current_index = 0; 41 | uint32_t total_voting_percent = 5; // 5% 42 | uint32_t duration_of_voting = 30; // voting duration (days) 43 | uint32_t max_duration_of_funding = 180; // funding duration (days) 44 | uint32_t total_iteration_of_funding = 6; // 45 | EOSLIB_SERIALIZE( wps_env, (proposal_current_index)(total_voting_percent)(duration_of_voting)(max_duration_of_funding)(total_iteration_of_funding) ) 46 | }; 47 | 48 | typedef eosio::multi_index< N(votings), voting_info > voting_table; 49 | typedef eosio::singleton< N(wpsglobal), wps_env > wps_env_singleton; 50 | 51 | class wps_contract : public eosio::contract { 52 | public: 53 | explicit wps_contract(action_name self); 54 | 55 | // proposer 56 | // @abi action 57 | void regproposer(account_name account, const string& first_name, const string& last_name, 58 | const string& img_url, const string& bio, const string& country, const string& telegram, 59 | const string& website, const string& linkedin); 60 | 61 | //@abi action 62 | void editproposer(account_name account, const string& first_name, const string& last_name, 63 | const string& img_url, const string& bio, const string& country, const string& telegram, 64 | const string& website, const string& linkedin); 65 | 66 | //@abi action 67 | void rmvproposer(account_name account); 68 | 69 | //@abi action 70 | void claimfunds(account_name account, uint64_t proposal_id); 71 | 72 | // proposal 73 | // @abi action 74 | void regproposal( 75 | account_name proposer, 76 | account_name committee, 77 | uint16_t subcategory, 78 | const string& title, 79 | const string& summary, 80 | const string& project_img_url, 81 | const string& description, 82 | const string& roadmap, 83 | uint64_t duration, 84 | const vector& members, 85 | const asset& funding_goal 86 | ); 87 | 88 | //@abi action 89 | void editproposal( 90 | account_name proposer, 91 | account_name committee, 92 | uint16_t subcategory, 93 | const string& title, 94 | const string& summary, 95 | const string& project_img_url, 96 | const string& description, 97 | const string& roadmap, 98 | uint64_t duration, 99 | const vector& members, 100 | const asset& funding_goal 101 | ); 102 | 103 | //@abi action 104 | void rmvproposal(account_name proposer); 105 | 106 | // reviewer 107 | //@abi action 108 | void regreviewer(account_name committee, account_name reviewer, const string& first_name, const string& last_name); 109 | 110 | //@abi action 111 | void editreviewer(account_name committee, account_name reviewer, const string& first_name, const string& last_name); 112 | 113 | //@abi action 114 | void rmvreviewer(account_name committee, const account_name reviewer); 115 | 116 | //@abi action 117 | void acceptprop(account_name reviewer, uint64_t proposal_id); 118 | 119 | //@abi action 120 | void rejectprop(account_name reviewer, uint64_t proposal_id, const string& reason); 121 | 122 | //@abi action 123 | void checkvote(account_name reviewer, uint64_t proposal_id); 124 | 125 | //@abi action 126 | void approve(account_name reviewer, uint64_t proposal_id); 127 | 128 | //@abi action 129 | void rmvreject(account_name reviewer, uint64_t proposal_id); 130 | 131 | // @abi action 132 | void rmvcompleted(account_name reviewer, uint64_t proposal_id); 133 | 134 | 135 | // vote 136 | //@abi action 137 | void vote(account_name voter, uint64_t proposal_id, bool is_agree); 138 | 139 | //@abi action 140 | void unvote(account_name voter, uint64_t proposal_id); 141 | 142 | 143 | // committee 144 | // @abi action 145 | void setwpsenv(uint32_t total_voting_percent, uint32_t duration_of_voting, uint32_t max_duration_of_funding, uint32_t total_iteration_of_funding); 146 | 147 | // @abi action 148 | void regcommittee(account_name committeeman, const string& category, bool is_oversight); 149 | 150 | // @abi action 151 | void edcommittee(account_name committeeman, const string& category, bool is_oversight); 152 | 153 | // @abi action 154 | void rmvcommittee(account_name committeeman); 155 | 156 | //@abi action 157 | void rejectfund(account_name committeeman, uint64_t proposal_id, const string& reason); 158 | 159 | 160 | // watchman 161 | //@abi action 162 | void commitvote(account_name watchman, uint64_t proposal_id, uint64_t total_votes, uint64_t agree_votes, uint64_t disagree_votes); 163 | 164 | //@abi action 165 | void rollbackvote(account_name watchman, uint64_t proposal_id, uint64_t total_votes, uint64_t agree_votes, uint64_t disagree_votes); 166 | 167 | //@abi action 168 | void checkexpire(account_name watchman, uint64_t proposal_id); 169 | 170 | private: 171 | wps_env_singleton m_wps_env_global; 172 | }; 173 | } // eosiowps 174 | -------------------------------------------------------------------------------- /contracts/eosio.wps/include/eosio.wps/proposal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace eosiowps { 10 | using eosio::asset; 11 | using eosio::indexed_by; 12 | using eosio::const_mem_fun; 13 | using std::string; 14 | using std::vector; 15 | 16 | struct PROPOSAL_STATUS { 17 | const static uint8_t PENDING = 1; 18 | const static uint8_t REJECTED = 2; 19 | const static uint8_t ON_VOTE = 3; 20 | const static uint8_t FINISHED_VOTING = 4; 21 | const static uint8_t CHECK_VOTE = 5; // check count of votes 22 | const static uint8_t CHECKED_VOTE = 6; // checked count of votes by platform 23 | const static uint8_t APPROVED = 7; // approve 24 | const static uint8_t COMPLETED = 8; 25 | }; 26 | 27 | //@abi table 28 | struct proposal { 29 | account_name proposer; // proposer 30 | uint64_t id; 31 | account_name committee; // committee 32 | string category; // category 33 | uint16_t subcategory; // subcategory 34 | string title; // title 35 | string summary; // summary 36 | string project_img_url; // project image or video url 37 | string description; // overview 38 | string roadmap; // roadmap 39 | uint64_t duration; // duration 40 | vector members; // linkedin 41 | asset funding_goal; // amount of EOS 42 | uint64_t total_votes; // total votes 43 | uint64_t agree_votes; // agree votes 44 | uint64_t disagree_votes; // disagree votes 45 | uint8_t status; // status 46 | uint64_t vote_start_time; // time when voting starts (seconds) 47 | uint64_t fund_start_time; // time when funding starts (seconds) 48 | uint8_t iteration_of_funding; // number of iteration 49 | uint64_t primary_key() const { return proposer; } 50 | uint64_t by_id() const { return static_cast(id); } 51 | EOSLIB_SERIALIZE( proposal, (proposer)(id)(committee)(category)(subcategory)(title)(summary)(project_img_url)(description)(roadmap)(duration)(members)(funding_goal) 52 | (total_votes)(agree_votes)(disagree_votes)(status)(vote_start_time)(fund_start_time)(iteration_of_funding) ) 53 | }; 54 | 55 | typedef eosio::multi_index< N(proposals), proposal, 56 | indexed_by< N(idx), const_mem_fun > 57 | > proposal_table; 58 | 59 | typedef eosio::multi_index< N(rejectedpros), proposal, 60 | indexed_by< N(idx), const_mem_fun > 61 | > rejected_proposal_table; 62 | 63 | typedef eosio::multi_index< N(finishedpros), proposal, 64 | indexed_by< N(idx), const_mem_fun > 65 | > finished_proposal_table; 66 | 67 | } // eosiowps -------------------------------------------------------------------------------- /contracts/eosio.wps/include/eosio.wps/proposer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace eosiowps { 9 | using std::string; 10 | using std::vector; 11 | 12 | //@abi table 13 | struct proposer { 14 | account_name account; 15 | string first_name; 16 | string last_name; 17 | string img_url; 18 | string bio; 19 | string country; 20 | string telegram; 21 | string website; 22 | string linkedin; 23 | uint64_t last_claim_time; 24 | uint64_t primary_key() const { return account; } 25 | EOSLIB_SERIALIZE( proposer, (account)(first_name)(last_name)(img_url)(bio)(country)(telegram)(website)(linkedin)(last_claim_time) ) 26 | }; 27 | typedef eosio::multi_index proposer_table; 28 | 29 | } // eosiowps -------------------------------------------------------------------------------- /contracts/eosio.wps/include/eosio.wps/reviewer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace eosiowps { 9 | using std::string; 10 | using std::vector; 11 | 12 | //@abi table 13 | struct reviewer { 14 | account_name account; 15 | account_name committee; 16 | string first_name; 17 | string last_name; 18 | uint64_t primary_key() const { return account; } 19 | EOSLIB_SERIALIZE( reviewer, (account)(committee)(first_name)(last_name) ) 20 | }; 21 | 22 | typedef eosio::multi_index< N(reviewers), reviewer> reviewer_table; 23 | } // eosiowps -------------------------------------------------------------------------------- /contracts/eosio.wps/src/committee.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace eosiowps { 6 | 7 | // @abi action 8 | void wps_contract::setwpsenv(uint32_t total_voting_percent, uint32_t duration_of_voting, uint32_t max_duration_of_funding, uint32_t total_iteration_of_funding) { 9 | //registration of committee requires contract account permissions 10 | require_auth(_self); 11 | 12 | eosio_assert(total_voting_percent >= 5, "total_voting_percent should be more than equal 5 long"); 13 | eosio_assert(duration_of_voting > 0, "duration_of_voting should be more than 0"); 14 | eosio_assert(max_duration_of_funding > 0, "max_duration_of_funding should be more than 0"); 15 | eosio_assert(total_iteration_of_funding > 0, "total_iteration_of_funding should be more than 0"); 16 | 17 | auto wps_env = m_wps_env_global.get(); 18 | 19 | wps_env.total_voting_percent = total_voting_percent; 20 | wps_env.duration_of_voting = duration_of_voting; 21 | wps_env.max_duration_of_funding = max_duration_of_funding; 22 | wps_env.total_iteration_of_funding = total_iteration_of_funding; 23 | 24 | m_wps_env_global.set( wps_env, _self ); 25 | } 26 | 27 | // @abi action 28 | void wps_contract::regcommittee(account_name committeeman, const string& category, bool is_oversight) { 29 | //registration of committee requires contract account permissions 30 | require_auth(_self); 31 | 32 | //verify that the committee account exists 33 | eosio_assert(is_account(committeeman), "committeeman account doesn't exist"); 34 | 35 | //verify that the size of the category string is not too long/short 36 | eosio_assert(category.size() > 0, "category should be more than 0 characters long"); 37 | eosio_assert(category.size() < 64, "category should be less than 64 characters long"); 38 | 39 | //creates the committee table if it doesn't exist already 40 | committee_table committees(_self, _self); 41 | 42 | auto itr = committees.find(committeeman); 43 | // verify that the account doesn't already exist in the table 44 | eosio_assert(itr == committees.end(), "This account has already been registered as a committee"); 45 | 46 | //add to the table 47 | committees.emplace(_self, [&](auto& committee){ 48 | committee.committeeman = committeeman; 49 | committee.category = category; 50 | committee.is_oversight = is_oversight; 51 | }); 52 | } 53 | 54 | // @abi action 55 | void wps_contract::edcommittee(account_name committeeman, const string& category, bool is_oversight) { 56 | //editing committee info requires contract account permissions 57 | require_auth(_self); 58 | 59 | //verify that the committee account exists 60 | eosio_assert(is_account(committeeman), "committee account doesn't exist"); 61 | 62 | //verify that the size of the category string is not too long/short 63 | eosio_assert(category.size() > 0, "category should be more than 0 characters long"); 64 | eosio_assert(category.size() < 64, "category should be less than 64 characters long"); 65 | 66 | //creates the committee table if it doesn't exist already 67 | committee_table committees(_self, _self); 68 | 69 | auto itr = committees.find(committeeman); 70 | // verify that the account doesn't already exist in the table 71 | eosio_assert(itr != committees.end(), "Account not found in committee table"); 72 | 73 | //add to the table 74 | committees.modify(itr, 0, [&](auto& committee){ 75 | committee.committeeman = committeeman; 76 | committee.category = category; 77 | committee.is_oversight = is_oversight; 78 | }); 79 | } 80 | 81 | // @abi action 82 | void wps_contract::rmvcommittee(account_name committeeman) { 83 | require_auth(_self); 84 | 85 | eosio_assert(is_account(committeeman), "committeeman account doesn't exist"); 86 | 87 | committee_table committees(_self, _self); 88 | auto itr = committees.find(committeeman); 89 | 90 | eosio_assert(itr != committees.end(), "Account not found in committee table"); 91 | committees.erase( itr ); 92 | } 93 | 94 | // @abi action 95 | void wps_contract::rejectfund(account_name committeeman, uint64_t proposal_id, const string& reason){ 96 | require_auth(committeeman); 97 | 98 | eosio_assert(reason.size() > 0, "must provide a brief reason"); 99 | eosio_assert(reason.size() < 256, "reason is too long"); 100 | 101 | committee_table committees(_self, _self); 102 | 103 | auto itr = committees.find(committeeman); 104 | // verify that the committee is on committee table 105 | eosio_assert(itr != committees.end(), "Account not found in committee table"); 106 | 107 | proposal_table proposals(_self, _self); 108 | 109 | auto idx_index = proposals.get_index(); 110 | auto itr_proposal = idx_index.find(proposal_id); 111 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 112 | 113 | auto& proposal = (*itr_proposal); 114 | 115 | eosio_assert(proposal.committee == (*itr).committeeman || (*itr).is_oversight, "Committee is not associated with this proposal"); 116 | eosio_assert(proposal.status == PROPOSAL_STATUS::APPROVED, "Proposal::status is not PROPOSAL_STATUS::APPROVED"); 117 | 118 | rejected_proposal_table rejected_proposals(_self, _self); 119 | 120 | //add to the table 121 | rejected_proposals.emplace(committeeman, [&](auto& _proposal){ 122 | _proposal = proposal; 123 | _proposal.status = PROPOSAL_STATUS::REJECTED; 124 | }); 125 | 126 | idx_index.erase(itr_proposal); 127 | } 128 | } //eosiowps -------------------------------------------------------------------------------- /contracts/eosio.wps/src/eosio.wps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "proposer.cpp" 5 | #include "proposal.cpp" 6 | #include "reviewer.cpp" 7 | #include "vote.cpp" 8 | #include "committee.cpp" 9 | #include "watchman.cpp" 10 | 11 | namespace eosiowps { 12 | wps_contract::wps_contract(action_name self) : contract(self), m_wps_env_global(self, self) { 13 | if (m_wps_env_global.exists() == false) { 14 | m_wps_env_global.set( wps_env(), _self ); 15 | } 16 | } 17 | }; 18 | 19 | EOSIO_ABI( eosiowps::wps_contract, 20 | // proposer.cpp 21 | (regproposer)(editproposer)(rmvproposer)(claimfunds) 22 | // proposal.cpp 23 | (regproposal)(editproposal)(rmvproposal) 24 | // reviewer.cpp 25 | (regreviewer)(editreviewer)(rmvreviewer)(acceptprop)(rejectprop)(checkvote)(approve)(rmvreject)(rmvcompleted) 26 | // vote.cpp 27 | (vote)(unvote) // (stake)(unstake) 28 | // committee.cpp 29 | (setwpsenv)(regcommittee)(edcommittee)(rmvcommittee)(rejectfund) 30 | // watchman.cpp 31 | (commitvote)(rollbackvote)(checkexpire) 32 | 33 | ) 34 | 35 | //To do: put a cap on the amount of funds that each committee can pull from eosio.wps 36 | //Refactor vote.cpp -------------------------------------------------------------------------------- /contracts/eosio.wps/src/vote.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace eosiowps { 7 | // @abi action 8 | void wps_contract::vote(account_name voter, uint64_t proposal_id, bool is_agree) { 9 | require_auth(voter); 10 | 11 | proposal_table proposals(_self, _self); 12 | auto idx_index = proposals.get_index(); 13 | auto itr_proposal = idx_index.find(proposal_id); 14 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 15 | eosio_assert((*itr_proposal).status == PROPOSAL_STATUS::ON_VOTE, "Proposal::status is not PROPOSAL_STATUS::ON_VOTE"); 16 | //eosio_assert((*itr_proposal).status != PROPOSAL_STATUS::FINISHED_VOTING, "The voting period for this proposal has expired."); 17 | 18 | auto current_time = now(); 19 | auto wps_env = m_wps_env_global.get(); 20 | auto duration_of_voting = wps_env.duration_of_voting * seconds_per_day; 21 | 22 | if(current_time - (*itr_proposal).vote_start_time >= duration_of_voting) { 23 | idx_index.modify(itr_proposal, 0, [&](auto &proposal) { 24 | proposal.status = PROPOSAL_STATUS::FINISHED_VOTING; 25 | }); 26 | } 27 | 28 | voting_table voting(_self, _self); 29 | auto itr = voting.find(proposal_id); 30 | if (itr == voting.end()) { 31 | voting.emplace(_self, [&](auto& vote_info) { 32 | vote_info.proposal_id = proposal_id; 33 | if (is_agree == true) { 34 | vote_info.agrees.emplace_back(voter); 35 | } else { 36 | vote_info.disagrees.emplace_back(voter); 37 | } 38 | }); 39 | } else { 40 | voting.modify(itr, 0, [&](auto& vote_info) { 41 | if (is_agree == true) { 42 | auto itr_agree = std::find(vote_info.agrees.begin(), vote_info.agrees.end(), voter); 43 | eosio_assert(itr_agree == vote_info.agrees.end(), "duplicate agree vote"); 44 | vote_info.agrees.emplace_back(voter); 45 | 46 | auto itr_disagree = std::find(vote_info.disagrees.begin(), vote_info.disagrees.end(), voter); 47 | if (itr_disagree != vote_info.disagrees.end()) { 48 | vote_info.disagrees.erase(itr_disagree); 49 | } 50 | } else { 51 | auto itr_disagree = std::find(vote_info.disagrees.begin(), vote_info.disagrees.end(), voter); 52 | eosio_assert(itr_disagree == vote_info.disagrees.end(), "duplicate disagree vote"); 53 | vote_info.disagrees.emplace_back(voter); 54 | auto itr_agree = std::find(vote_info.agrees.begin(), vote_info.agrees.end(), voter); 55 | if (itr_agree != vote_info.agrees.end()) { 56 | vote_info.agrees.erase(itr_agree); 57 | } 58 | } 59 | }); 60 | } 61 | } 62 | 63 | // @abi action 64 | void wps_contract::unvote(account_name voter, uint64_t proposal_id) { 65 | require_auth(voter); 66 | 67 | proposal_table proposals(_self, _self); 68 | auto idx_index = proposals.get_index(); 69 | auto itr_proposal = idx_index.find(proposal_id); 70 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 71 | eosio_assert((*itr_proposal).status == PROPOSAL_STATUS::ON_VOTE, "Proposal::status is not proposal_status::PROPOSAL_STATUS"); 72 | //eosio_assert((*itr_proposal).status != PROPOSAL_STATUS::FINISHED_VOTING, "The voting period for this proposal has expired."); 73 | 74 | auto current_time = now(); 75 | auto wps_env = m_wps_env_global.get(); 76 | auto duration_of_voting = wps_env.duration_of_voting * seconds_per_day; 77 | 78 | if(current_time - (*itr_proposal).vote_start_time >= duration_of_voting) { 79 | idx_index.modify(itr_proposal, 0, [&](auto &proposal) { 80 | proposal.status = PROPOSAL_STATUS::FINISHED_VOTING; 81 | }); 82 | } 83 | 84 | voting_table voting(_self, _self); 85 | auto itr = voting.find(proposal_id); 86 | if (itr != voting.end()) { 87 | voting.modify(itr, 0, [&](auto& vote_info) { 88 | auto itr_agree = std::find(vote_info.agrees.begin(), vote_info.agrees.end(), voter); 89 | if(itr_agree != vote_info.agrees.end()) { 90 | vote_info.agrees.erase(itr_agree); 91 | return; 92 | } 93 | auto itr_disagree = std::find(vote_info.disagrees.begin(), vote_info.disagrees.end(), voter); 94 | if(itr_disagree != vote_info.disagrees.end()) { 95 | vote_info.disagrees.erase(itr_disagree); 96 | } 97 | }); 98 | } 99 | } 100 | } // eosiowps 101 | -------------------------------------------------------------------------------- /contracts/eosio.wps/src/watchman.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace eosiowps { 5 | 6 | // @abi action 7 | void wps_contract::commitvote(account_name watchman, uint64_t proposal_id, uint64_t total_votes, uint64_t agree_votes, uint64_t disagree_votes) { 8 | require_auth(watchman); 9 | 10 | committee_table committees(_self, _self); 11 | auto itr = committees.find(watchman); 12 | eosio_assert(itr != committees.end(), "Account not found in committees table"); 13 | eosio_assert((*itr).is_oversight, "account does not have oversight privileges"); 14 | 15 | proposal_table proposals(_self, _self); 16 | auto idx_index = proposals.get_index(); 17 | auto itr_proposal = idx_index.find(proposal_id); 18 | eosio_assert((*itr).is_oversight, "account does not have oversight privileges"); 19 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 20 | eosio_assert((*itr_proposal).status == PROPOSAL_STATUS::CHECK_VOTE, "Proposal's status is not CHECK_VOTE"); 21 | 22 | idx_index.modify(itr_proposal, 0, [&](auto& proposal){ 23 | proposal.status = PROPOSAL_STATUS::CHECKED_VOTE; 24 | proposal.total_votes = total_votes; 25 | proposal.agree_votes = agree_votes; 26 | proposal.disagree_votes = disagree_votes; 27 | }); 28 | }//action permission should be linked to a separate key 29 | 30 | // @abi action 31 | void wps_contract::rollbackvote(account_name watchman, uint64_t proposal_id, uint64_t total_votes, uint64_t agree_votes, uint64_t disagree_votes){ 32 | require_auth(watchman); 33 | 34 | committee_table committees(_self, _self); 35 | auto itr = committees.find(watchman); 36 | eosio_assert(itr != committees.end(), "Account not found in committees table"); 37 | eosio_assert((*itr).is_oversight, "account does not have oversight privileges"); 38 | 39 | proposal_table proposals(_self, _self); 40 | auto idx_index = proposals.get_index(); 41 | auto itr_proposal = idx_index.find(proposal_id); 42 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 43 | eosio_assert((*itr_proposal).status == PROPOSAL_STATUS::CHECK_VOTE, "Proposal's status is not CHECK_VOTE"); 44 | 45 | /* 46 | idx_index.modify(itr_proposal, 0, [&](auto& proposal) { 47 | proposal.status = PROPOSAL_STATUS::REJECTED; //when vote tally proves that threshold wasn't reached 48 | proposal.total_votes = total_votes; //send data to chain, proof of tally 49 | proposal.agree_votes = agree_votes; 50 | proposal.disagree_votes = disagree_votes; 51 | }); 52 | */ 53 | rejected_proposal_table rejected_proposals(_self, _self); 54 | rejected_proposals.emplace(_self, [&](auto& _proposal){ 55 | _proposal = (*itr_proposal); 56 | _proposal.total_votes = total_votes; //send data to chain, proof of tally 57 | _proposal.agree_votes = agree_votes; 58 | _proposal.disagree_votes = disagree_votes; 59 | _proposal.status = PROPOSAL_STATUS::REJECTED; 60 | 61 | }); 62 | idx_index.erase(itr_proposal); 63 | 64 | }//action permission should be linked to a separate key 65 | 66 | void wps_contract::checkexpire(account_name watchman, uint64_t proposal_id) { 67 | //require_auth(watchman); 68 | 69 | proposal_table proposals(_self, _self); 70 | auto idx_index = proposals.get_index(); 71 | auto itr_proposal = idx_index.find(proposal_id); 72 | eosio_assert(itr_proposal != idx_index.end(), "Proposal not found in proposal table"); 73 | eosio_assert((*itr_proposal).status == PROPOSAL_STATUS::ON_VOTE, "Proposal::status is not PROPOSAL_STATUS::ON_VOTE"); 74 | 75 | auto current_time = now(); 76 | auto wps_env = m_wps_env_global.get(); 77 | auto duration_of_voting = wps_env.duration_of_voting * seconds_per_day; 78 | 79 | if(current_time - (*itr_proposal).vote_start_time > duration_of_voting){ 80 | rejected_proposal_table rejected_proposals(_self, _self); 81 | rejected_proposals.emplace(_self, [&](auto& _proposal){ 82 | _proposal = (*itr_proposal); 83 | _proposal.status = PROPOSAL_STATUS::REJECTED; 84 | }); 85 | idx_index.erase(itr_proposal); 86 | } 87 | } 88 | } //eosiowps -------------------------------------------------------------------------------- /contracts/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.5 ) 2 | 3 | list(APPEND CMAKE_MODULE_PATH ${EOSIO_ROOT}/lib/cmake) 4 | include(EosioTester) 5 | 6 | ### check the version of EOSIO 7 | string(FIND "${EOSIO_VERSION}" "${EOSIO_DEPENDENCY}" output) 8 | if (NOT "${output}" EQUAL 0) 9 | message(FATAL_ERROR "Incorrect EOSIO version, please use version ${EOSIO_DEPENDENCY}.x") 10 | endif() 11 | 12 | 13 | enable_testing() 14 | 15 | set(ROOT_DIR ${CMAKE_SOURCE_DIR}/..) 16 | configure_file(${CMAKE_SOURCE_DIR}/contracts.hpp.in ${CMAKE_SOURCE_DIR}/contracts.hpp) 17 | 18 | include_directories(${CMAKE_BINARY_DIR}) 19 | 20 | file(GLOB UNIT_TESTS "*.cpp" "*.hpp") 21 | 22 | add_eosio_test( unit_test ${UNIT_TESTS} ) 23 | -------------------------------------------------------------------------------- /contracts/tests/contracts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace eosio { namespace testing { 5 | 6 | struct contracts { 7 | static std::vector wps_wasm() { return read_wasm("/Users/yepp4you/Work/blockchain/wps-backend/contracts/build/tests/../../eosio.wps/bin/eosio.wps/eosio.wps.wasm"); } 8 | static std::string wps_wast() { return read_wast("/Users/yepp4you/Work/blockchain/wps-backend/contracts/build/tests/../../eosio.wps/bin/eosio.wps/eosio.wps.wast"); } 9 | static std::vector wps_abi() { return read_abi("/Users/yepp4you/Work/blockchain/wps-backend/contracts/build/tests/../../eosio.wps/bin/eosio.wps/eosio.wps.abi"); } 10 | 11 | struct util { 12 | static std::vector test_api_wasm() { return read_wasm("/Users/yepp4you/Work/blockchain/wps-backend/contracts/tests/test_contracts/test_api.wasm"); } 13 | static std::vector exchange_wasm() { return read_wasm("/Users/yepp4you/Work/blockchain/wps-backend/contracts/tests/test_contracts/exchange.wasm"); } 14 | 15 | static std::vector token_wasm() { return read_wasm("/Users/yepp4you/Work/blockchain/wps-backend/contracts/tests/test_contracts/eosio.token.wasm"); } 16 | static std::vector token_abi() { return read_abi("/Users/yepp4you/Work/blockchain/wps-backend/contracts/tests/test_contracts/eosio.token.abi"); } 17 | }; 18 | }; 19 | }} //ns eosio::testing 20 | -------------------------------------------------------------------------------- /contracts/tests/contracts.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace eosio { namespace testing { 5 | 6 | struct contracts { 7 | static std::vector wps_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../../eosio.wps/bin/eosio.wps/eosio.wps.wasm"); } 8 | static std::string wps_wast() { return read_wast("${CMAKE_BINARY_DIR}/../../eosio.wps/bin/eosio.wps/eosio.wps.wast"); } 9 | static std::vector wps_abi() { return read_abi("${CMAKE_BINARY_DIR}/../../eosio.wps/bin/eosio.wps/eosio.wps.abi"); } 10 | 11 | struct util { 12 | static std::vector test_api_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/test_api.wasm"); } 13 | static std::vector exchange_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/exchange.wasm"); } 14 | 15 | static std::vector token_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token.wasm"); } 16 | static std::vector token_abi() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token.abi"); } 17 | }; 18 | }; 19 | }} //ns eosio::testing 20 | -------------------------------------------------------------------------------- /contracts/tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void translate_fc_exception(const fc::exception &e) { 8 | std::cerr << "\033[33m" << e.to_detail_string() << "\033[0m" << std::endl; 9 | BOOST_TEST_FAIL("Caught Unexpected Exception"); 10 | } 11 | 12 | boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { 13 | // Turn off blockchain logging if no --verbose parameter is not added 14 | // To have verbose enabled, call "tests/chain_test -- --verbose" 15 | bool is_verbose = false; 16 | std::string verbose_arg = "--verbose"; 17 | for (int i = 0; i < argc; i++) { 18 | if (verbose_arg == argv[i]) { 19 | is_verbose = true; 20 | break; 21 | } 22 | } 23 | if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); 24 | 25 | // Register fc::exception translator 26 | boost::unit_test::unit_test_monitor.template register_exception_translator(&translate_fc_exception); 27 | 28 | std::srand(time(NULL)); 29 | std::cout << "Random number generator seeded to " << time(NULL) << std::endl; 30 | return nullptr; 31 | } -------------------------------------------------------------------------------- /contracts/tests/test.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include "contracts.hpp" 12 | 13 | using namespace eosio::testing; 14 | using namespace eosio; 15 | using namespace eosio::chain; 16 | using namespace eosio::testing; 17 | using namespace fc; 18 | 19 | using mvo = fc::mutable_variant_object; 20 | 21 | const uint32_t seconds_per_day = 60 * 60 * 24; 22 | 23 | struct __attribute((packed)) committee_t { 24 | account_name committeeman; 25 | string category; 26 | bool is_oversight; 27 | uint64_t primary_key() const { return committeeman; } 28 | }; 29 | FC_REFLECT( committee_t, (committeeman)(category)(is_oversight) ); 30 | 31 | struct wps_env_t { 32 | uint64_t proposal_current_index = 0; 33 | uint32_t total_voting_percent = 5; // 5% 34 | uint32_t duration_of_voting = 30; // voting duration (days) 35 | uint32_t max_duration_of_funding = 180; // funding duration (days) 36 | uint32_t total_iteration_of_funding = 6; // 37 | }; 38 | FC_REFLECT( wps_env_t, (proposal_current_index)(total_voting_percent)(duration_of_voting)(max_duration_of_funding)(total_iteration_of_funding) ); 39 | 40 | struct reviewer_t { 41 | account_name account; 42 | account_name committee; 43 | string first_name; 44 | string last_name; 45 | uint64_t primary_key() const { return account; } 46 | }; 47 | FC_REFLECT( reviewer_t, (account)(committee)(first_name)(last_name) ); 48 | 49 | struct proposer_t { 50 | account_name account; 51 | string first_name; 52 | string last_name; 53 | string img_url; 54 | string bio; 55 | string country; 56 | string telegram; 57 | string website; 58 | string linkedin; 59 | uint64_t last_claim_time; 60 | uint64_t primary_key() const { return account; } 61 | }; 62 | FC_REFLECT( proposer_t, (account)(first_name)(last_name)(img_url)(bio)(country)(telegram)(website)(linkedin)(last_claim_time) ) 63 | 64 | struct PROPOSAL_STATUS_T { 65 | const static uint8_t PENDING = 1; 66 | const static uint8_t REJECTED = 2; 67 | const static uint8_t ON_VOTE = 3; 68 | const static uint8_t FINISHED_VOTING = 4; 69 | const static uint8_t CHECK_VOTE = 5; // check count of votes 70 | const static uint8_t CHECKED_VOTE = 6; // checked count of votes by platform 71 | const static uint8_t APPROVED = 7; // approve 72 | const static uint8_t COMPLETED = 8; 73 | }; 74 | 75 | struct proposal_t { 76 | account_name proposer; // proposer 77 | uint64_t id; 78 | account_name committee; // committee 79 | string category; // category 80 | uint16_t subcategory; // subcategory 81 | string title; // title 82 | string summary; // summary 83 | string project_img_url; // project image or video url 84 | string description; // overview 85 | string roadmap; // financial 86 | uint64_t duration; // duration 87 | vector members; // linkedin 88 | asset funding_goal; // amount of EOS 89 | uint64_t total_votes; // total votes 90 | uint64_t agree_votes; // total votes 91 | uint64_t disagree_votes; // total votes 92 | uint8_t status; // status 93 | uint64_t vote_start_time; // time when voting starts (seconds) 94 | uint64_t fund_start_time; // time when funding starts (seconds) 95 | uint8_t iteration_of_funding; // number of iteration 96 | uint64_t primary_key() const { return proposer; } 97 | uint64_t by_id() const { return static_cast(id); } 98 | }; 99 | FC_REFLECT( proposal_t, (proposer)(id)(committee)(category)(subcategory)(title)(summary)(project_img_url)(description)(roadmap)(duration)(members) 100 | (funding_goal)(total_votes)(agree_votes)(disagree_votes)(status)(vote_start_time)(fund_start_time)(iteration_of_funding) ) 101 | 102 | 103 | struct voting_info_t { 104 | uint64_t proposal_id; 105 | vector agrees; 106 | vector disagrees; 107 | uint64_t primary_key() const { return proposal_id; } 108 | }; 109 | FC_REFLECT( voting_info_t, (proposal_id)(agrees)(disagrees) ) -------------------------------------------------------------------------------- /contracts/tests/test_committee.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | class committee_tester : public tester { 4 | public: 5 | committee_tester() { 6 | create_accounts( {N(eosio.token), N(eosio.wps), N(eosio.saving), N(committee1), N(committee2)} ); 7 | produce_block(); 8 | 9 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 10 | mutable_variant_object() 11 | ("account", "eosio.token") 12 | ("is_priv", 1) 13 | ); 14 | 15 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 16 | mutable_variant_object() 17 | ("account", "eosio.wps") 18 | ("is_priv", 1) 19 | ); 20 | 21 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 22 | mutable_variant_object() 23 | ("account", "eosio.saving") 24 | ("is_priv", 1) 25 | ); 26 | 27 | set_code( N(eosio.token), contracts::util::token_wasm() ); 28 | set_abi( N(eosio.token), contracts::util::token_abi().data() ); 29 | 30 | set_code( N(eosio.wps), contracts::wps_wasm() ); 31 | set_abi( N(eosio.wps), contracts::wps_abi().data() ); 32 | 33 | produce_blocks(); 34 | 35 | const auto& accnt = control->db().get( N(eosio.wps) ); 36 | abi_def abi; 37 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 38 | abi_ser.set_abi(abi, fc::seconds(1)); 39 | } 40 | 41 | bool get_committee(committee_t& committeeman, account_name name) { 42 | return get_table_entry(committeeman, N(eosio.wps), N(eosio.wps), N(committees), name); 43 | } 44 | 45 | bool get_wpsenv(wps_env_t& wpsenv ) { 46 | return get_table_entry(wpsenv, N(eosio.wps), N(eosio.wps), N(wpsglobal), N(wpsglobal)); 47 | } 48 | 49 | abi_serializer abi_ser; 50 | }; 51 | 52 | BOOST_AUTO_TEST_SUITE(committee_tests) 53 | 54 | BOOST_FIXTURE_TEST_CASE( manage_committee, committee_tester ) try { 55 | auto committeeman1_1 = mvo() 56 | ("committeeman", "committee1") 57 | ("category", "emergency") 58 | ("is_oversight", 1); 59 | 60 | auto committeeman2_1 = mvo() 61 | ("committeeman", "committee2") 62 | ("category", "game") 63 | ("is_oversight", 0); 64 | 65 | auto committeeman1_2 = mvo() 66 | ("committeeman", "committee1") 67 | ("category", "emergency2") 68 | ("is_oversight", 1); 69 | 70 | auto committeeman2_2 = mvo() 71 | ("committeeman", "committee2") 72 | ("category", "game2") 73 | ("is_oversight", 0); 74 | 75 | auto wpsenv = mvo() 76 | ("total_voting_percent", 10) 77 | ("duration_of_voting", 30) 78 | ("max_duration_of_funding", 180) 79 | ("total_iteration_of_funding", 6) 80 | ("proposal_current_index", 0); 81 | 82 | auto trace = base_tester::push_action( N(eosio.wps), N(setwpsenv), N(eosio.wps), wpsenv); 83 | 84 | produce_block(); 85 | 86 | wps_env_t _wpsenv; 87 | get_wpsenv(_wpsenv); 88 | REQUIRE_MATCHING_OBJECT(_wpsenv, wpsenv); 89 | 90 | trace = base_tester::push_action( N(eosio.wps), N(regcommittee), N(eosio.wps), committeeman1_1); 91 | trace = base_tester::push_action( N(eosio.wps), N(regcommittee), N(eosio.wps), committeeman2_1); 92 | 93 | produce_block(); 94 | 95 | committee_t t1_1; 96 | committee_t t2_1; 97 | get_committee(t1_1, N(committee1)); 98 | get_committee(t2_1, N(committee2)); 99 | 100 | REQUIRE_MATCHING_OBJECT(t1_1, committeeman1_1); 101 | REQUIRE_MATCHING_OBJECT(t2_1, committeeman2_1); 102 | 103 | trace = base_tester::push_action( N(eosio.wps), N(edcommittee), N(eosio.wps), committeeman1_2); 104 | trace = base_tester::push_action( N(eosio.wps), N(edcommittee), N(eosio.wps), committeeman2_2); 105 | 106 | produce_block(); 107 | 108 | committee_t t1_2; 109 | committee_t t2_2; 110 | get_committee(t1_2, N(committee1)); 111 | get_committee(t2_2, N(committee2)); 112 | 113 | REQUIRE_MATCHING_OBJECT(t1_2, committeeman1_2); 114 | REQUIRE_MATCHING_OBJECT(t2_2, committeeman2_2); 115 | 116 | trace = base_tester::push_action( N(eosio.wps), N(rmvcommittee), N(eosio.wps), mvo()("committeeman", "committee2")); 117 | produce_block(); 118 | 119 | /* 120 | try { 121 | get_committee(t2_2, N(committee2)); 122 | } catch(...) { 123 | BOOST_TEST_MESSAGE("exception"); 124 | } 125 | */ 126 | 127 | // BOOST_TEST_MESSAGE( fc::json::to_pretty_string(trace) ); 128 | // BOOST_TEST_MESSAGE( fc::json::to_pretty_string(t2_2) ); 129 | 130 | } FC_LOG_AND_RETHROW() 131 | 132 | BOOST_AUTO_TEST_SUITE_END() 133 | -------------------------------------------------------------------------------- /contracts/tests/test_contracts/eosio.token.abi: -------------------------------------------------------------------------------- 1 | { 2 | "version": "eosio::abi/1.0", 3 | "types": [{ 4 | "new_type_name": "account_name", 5 | "type": "name" 6 | }], 7 | "structs": [{ 8 | "name": "transfer", 9 | "base": "", 10 | "fields": [ 11 | {"name":"from", "type":"account_name"}, 12 | {"name":"to", "type":"account_name"}, 13 | {"name":"quantity", "type":"asset"}, 14 | {"name":"memo", "type":"string"} 15 | ] 16 | },{ 17 | "name": "create", 18 | "base": "", 19 | "fields": [ 20 | {"name":"issuer", "type":"account_name"}, 21 | {"name":"maximum_supply", "type":"asset"} 22 | ] 23 | },{ 24 | "name": "issue", 25 | "base": "", 26 | "fields": [ 27 | {"name":"to", "type":"account_name"}, 28 | {"name":"quantity", "type":"asset"}, 29 | {"name":"memo", "type":"string"} 30 | ] 31 | },{ 32 | "name": "retire", 33 | "base": "", 34 | "fields": [ 35 | {"name":"quantity", "type":"asset"}, 36 | {"name":"memo", "type":"string"} 37 | ] 38 | },{ 39 | "name": "close", 40 | "base": "", 41 | "fields": [ 42 | {"name":"owner", "type":"account_name"}, 43 | {"name":"symbol", "type":"symbol"}, 44 | ] 45 | },{ 46 | "name": "account", 47 | "base": "", 48 | "fields": [ 49 | {"name":"balance", "type":"asset"} 50 | ] 51 | },{ 52 | "name": "currency_stats", 53 | "base": "", 54 | "fields": [ 55 | {"name":"supply", "type":"asset"}, 56 | {"name":"max_supply", "type":"asset"}, 57 | {"name":"issuer", "type":"account_name"} 58 | ] 59 | } 60 | ], 61 | "actions": [{ 62 | "name": "transfer", 63 | "type": "transfer", 64 | "ricardian_contract": "" 65 | },{ 66 | "name": "issue", 67 | "type": "issue", 68 | "ricardian_contract": "" 69 | },{ 70 | "name": "retire", 71 | "type": "retire", 72 | "ricardian_contract": "" 73 | }, { 74 | "name": "create", 75 | "type": "create", 76 | "ricardian_contract": "" 77 | }, { 78 | "name": "close", 79 | "type": "close", 80 | "ricardian_contract": "" 81 | } 82 | 83 | ], 84 | "tables": [{ 85 | "name": "accounts", 86 | "type": "account", 87 | "index_type": "i64", 88 | "key_names" : ["currency"], 89 | "key_types" : ["uint64"] 90 | },{ 91 | "name": "stat", 92 | "type": "currency_stats", 93 | "index_type": "i64", 94 | "key_names" : ["currency"], 95 | "key_types" : ["uint64"] 96 | } 97 | ], 98 | "ricardian_clauses": [], 99 | "abi_extensions": [] 100 | } 101 | -------------------------------------------------------------------------------- /contracts/tests/test_contracts/eosio.token.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOS-BP-Developers/wps-backend/053fb79b86d9909fa160b84a4ce7f234a4932fed/contracts/tests/test_contracts/eosio.token.wasm -------------------------------------------------------------------------------- /contracts/tests/test_contracts/exchange.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOS-BP-Developers/wps-backend/053fb79b86d9909fa160b84a4ce7f234a4932fed/contracts/tests/test_contracts/exchange.wasm -------------------------------------------------------------------------------- /contracts/tests/test_contracts/test_api.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOS-BP-Developers/wps-backend/053fb79b86d9909fa160b84a4ce7f234a4932fed/contracts/tests/test_contracts/test_api.wasm -------------------------------------------------------------------------------- /contracts/tests/test_proposer.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | class proposer_tester : public tester { 4 | public: 5 | proposer_tester() { 6 | create_accounts( {N(eosio.token), N(eosio.wps), N(eosio.saving), N(proposer1), N(proposer2)} ); 7 | 8 | produce_block(); 9 | 10 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 11 | mutable_variant_object() 12 | ("account", "eosio.token") 13 | ("is_priv", 1) 14 | ); 15 | 16 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 17 | mutable_variant_object() 18 | ("account", "eosio.wps") 19 | ("is_priv", 1) 20 | ); 21 | 22 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 23 | mutable_variant_object() 24 | ("account", "eosio.saving") 25 | ("is_priv", 1) 26 | ); 27 | set_code( N(eosio.token), contracts::util::token_wasm() ); 28 | set_abi( N(eosio.token), contracts::util::token_abi().data() ); 29 | 30 | set_code( N(eosio.wps), contracts::wps_wasm() ); 31 | set_abi( N(eosio.wps), contracts::wps_abi().data() ); 32 | 33 | produce_blocks(); 34 | 35 | const auto& accnt = control->db().get( N(eosio.wps) ); 36 | abi_def abi; 37 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 38 | abi_ser.set_abi(abi, fc::seconds(1)); 39 | } 40 | 41 | bool get_proposer(proposer_t& proposer, account_name name) { 42 | return get_table_entry(proposer, N(eosio.wps), N(eosio.wps), N(proposers), name); 43 | } 44 | 45 | abi_serializer abi_ser; 46 | }; 47 | 48 | BOOST_AUTO_TEST_SUITE(proposer_tests) 49 | 50 | BOOST_FIXTURE_TEST_CASE( manage_proposer, proposer_tester ) try { 51 | auto proposer1_1 = mvo() 52 | ("account", "proposer1") 53 | ("first_name", "Thomas") 54 | ("last_name","Do") 55 | ("img_url", "http://www.google.com") 56 | ("bio", "hi~") 57 | ("country", "KR") 58 | ("telegram", "@yepp4you") 59 | ("website", "http://www.block.one") 60 | ("linkedin", "thomas-do-01911516a") 61 | ("last_claim_time", 0); 62 | 63 | auto proposer2_1 = mvo() 64 | ("account", "proposer2") 65 | ("first_name", "Thomas") 66 | ("last_name","Cox") 67 | ("img_url", "http://www.google.com") 68 | ("bio", "hi~") 69 | ("country", "KR") 70 | ("telegram", "@yepp4you") 71 | ("website", "http://www.block.one") 72 | ("linkedin", "thomasbcox") 73 | ("last_claim_time", 0); 74 | 75 | auto proposer1_2 = mvo() 76 | ("account", "proposer1") 77 | ("first_name", "Thomas2") 78 | ("last_name","Do2") 79 | ("img_url", "http://www.google.com") 80 | ("bio", "hi~") 81 | ("country", "KR") 82 | ("telegram", "@yepp4you") 83 | ("website", "http://www.block.one") 84 | ("linkedin", "thomas-do-01911516a") 85 | ("last_claim_time", 0); 86 | 87 | auto proposer2_2 = mvo() 88 | ("account", "proposer2") 89 | ("first_name", "Thomas2") 90 | ("last_name","Cox2") 91 | ("img_url", "http://www.google.com") 92 | ("bio", "hi~") 93 | ("country", "KR") 94 | ("telegram", "@yepp4you") 95 | ("website", "http://www.block.one") 96 | ("linkedin", "thomasbcox") 97 | ("last_claim_time", 0); 98 | 99 | base_tester::push_action( N(eosio.wps), N(regproposer), N(proposer1), proposer1_1); 100 | base_tester::push_action( N(eosio.wps), N(regproposer), N(proposer2), proposer2_1); 101 | 102 | produce_block(); 103 | 104 | proposer_t _c1_1; 105 | proposer_t _c2_1; 106 | get_proposer(_c1_1, N(proposer1)); 107 | get_proposer(_c2_1, N(proposer2)); 108 | 109 | REQUIRE_MATCHING_OBJECT(_c1_1, proposer1_1); 110 | REQUIRE_MATCHING_OBJECT(_c2_1, proposer2_1); 111 | 112 | base_tester::push_action( N(eosio.wps), N(editproposer), N(proposer1), proposer1_2); 113 | base_tester::push_action( N(eosio.wps), N(editproposer), N(proposer2), proposer2_2); 114 | 115 | produce_block(); 116 | 117 | proposer_t _c1_2; 118 | proposer_t _c2_2; 119 | get_proposer(_c1_2, N(proposer1)); 120 | get_proposer(_c2_2, N(proposer2)); 121 | 122 | REQUIRE_MATCHING_OBJECT(_c1_2, proposer1_2); 123 | REQUIRE_MATCHING_OBJECT(_c2_2, proposer2_2); 124 | 125 | base_tester::push_action( N(eosio.wps), N(rmvproposer), N(proposer2), mvo()("account", "proposer2")); 126 | produce_block(); 127 | 128 | } FC_LOG_AND_RETHROW() 129 | 130 | BOOST_AUTO_TEST_SUITE_END() 131 | -------------------------------------------------------------------------------- /contracts/tests/test_reviewer.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | class reviewer_tester : public tester { 4 | public: 5 | reviewer_tester() { 6 | create_accounts( {N(eosio.token), N(eosio.wps), N(eosio.saving), N(committee1), N(committee2), N(reviewer1), N(reviewer2)} ); 7 | 8 | produce_block(); 9 | 10 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 11 | mutable_variant_object() 12 | ("account", "eosio.token") 13 | ("is_priv", 1) 14 | ); 15 | 16 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 17 | mutable_variant_object() 18 | ("account", "eosio.wps") 19 | ("is_priv", 1) 20 | ); 21 | 22 | base_tester::push_action(config::system_account_name, N(setpriv), config::system_account_name, 23 | mutable_variant_object() 24 | ("account", "eosio.saving") 25 | ("is_priv", 1) 26 | ); 27 | set_code( N(eosio.token), contracts::util::token_wasm() ); 28 | set_abi( N(eosio.token), contracts::util::token_abi().data() ); 29 | 30 | set_code( N(eosio.wps), contracts::wps_wasm() ); 31 | set_abi( N(eosio.wps), contracts::wps_abi().data() ); 32 | 33 | produce_blocks(); 34 | 35 | const auto& accnt = control->db().get( N(eosio.wps) ); 36 | abi_def abi; 37 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 38 | abi_ser.set_abi(abi, fc::seconds(1)); 39 | 40 | init_committee(); 41 | } 42 | 43 | void init_committee() { 44 | auto committeeman1 = mvo() 45 | ("committeeman", "committee1") 46 | ("category", "emergency") 47 | ("is_oversight", 1); 48 | 49 | auto committeeman2 = mvo() 50 | ("committeeman", "committee2") 51 | ("category", "game") 52 | ("is_oversight", 0); 53 | 54 | auto wpsenv = mvo() 55 | ("total_voting_percent", 10) 56 | ("duration_of_voting", 30) 57 | ("max_duration_of_funding", 180) 58 | ("total_iteration_of_funding", 6) 59 | ("proposal_current_index", 0); 60 | 61 | auto trace = base_tester::push_action( N(eosio.wps), N(setwpsenv), N(eosio.wps), wpsenv); 62 | produce_block(); 63 | 64 | base_tester::push_action( N(eosio.wps), N(regcommittee), N(eosio.wps), committeeman1); 65 | base_tester::push_action( N(eosio.wps), N(regcommittee), N(eosio.wps), committeeman2); 66 | 67 | produce_block(); 68 | } 69 | 70 | void create_currency( name contract, name manager, asset maxsupply ) { 71 | auto act = mutable_variant_object() 72 | ("issuer", manager ) 73 | ("maximum_supply", maxsupply ); 74 | 75 | base_tester::push_action(contract, N(create), contract, act ); 76 | } 77 | 78 | void issue( name to, const asset& amount, name manager = config::system_account_name ) { 79 | base_tester::push_action( N(eosio.token), N(issue), manager, mutable_variant_object() 80 | ("to", to ) 81 | ("quantity", amount ) 82 | ("memo", "") 83 | ); 84 | } 85 | 86 | void transfer( name from, name to, const string& amount, name manager = config::system_account_name ) { 87 | base_tester::push_action( N(eosio.token), N(transfer), manager, mutable_variant_object() 88 | ("from", from) 89 | ("to", to ) 90 | ("quantity", asset::from_string(amount) ) 91 | ("memo", "") 92 | ); 93 | } 94 | 95 | asset get_balance( const account_name& act ) { 96 | //return get_currency_balance( config::system_account_name, symbol(CORE_SYMBOL), act ); 97 | //temporary code. current get_currency_balancy uses table name N(accounts) from currency.h 98 | //generic_currency table name is N(account). 99 | const auto& db = control->db(); 100 | const auto* tbl = db.find(boost::make_tuple(N(eosio.token), act, N(accounts))); 101 | share_type result = 0; 102 | 103 | // the balance is implied to be 0 if either the table or row does not exist 104 | if (tbl) { 105 | const auto *obj = db.find(boost::make_tuple(tbl->id, symbol(CORE_SYMBOL).to_symbol_code())); 106 | if (obj) { 107 | // balance is the first field in the serialization 108 | fc::datastream ds(obj->value.data(), obj->value.size()); 109 | fc::raw::unpack(ds, result); 110 | } 111 | } 112 | return asset( result, symbol(CORE_SYMBOL) ); 113 | } 114 | 115 | bool get_reviewer(reviewer_t& reviewer, account_name name) { 116 | return get_table_entry(reviewer, N(eosio.wps), N(eosio.wps), N(reviewers), name); 117 | } 118 | 119 | abi_serializer abi_ser; 120 | }; 121 | 122 | BOOST_AUTO_TEST_SUITE(reviewer_tests) 123 | 124 | BOOST_FIXTURE_TEST_CASE( manage_reviewer, reviewer_tester ) try { 125 | auto reviewer1_1 = mvo() 126 | ("reviewer", "reviewer1") 127 | ("committee", "committee1") 128 | ("first_name","Thomas") 129 | ("last_name", "Do"); 130 | 131 | auto reviewer2_1 = mvo() 132 | ("reviewer", "reviewer2") 133 | ("committee", "committee2") 134 | ("first_name","Thomas") 135 | ("last_name", "Cox"); 136 | 137 | auto reviewer1_2 = mvo() 138 | ("reviewer", "reviewer1") 139 | ("committee", "committee1") 140 | ("first_name","Thomas2") 141 | ("last_name", "Do2"); 142 | 143 | auto reviewer2_2 = mvo() 144 | ("reviewer", "reviewer2") 145 | ("committee", "committee2") 146 | ("first_name","Thomas2") 147 | ("last_name", "Cox2"); 148 | 149 | auto r1_1 = mvo() 150 | ("account", "reviewer1") 151 | ("committee", "committee1") 152 | ("first_name","Thomas") 153 | ("last_name", "Do"); 154 | 155 | auto r2_1 = mvo() 156 | ("account", "reviewer2") 157 | ("committee", "committee2") 158 | ("first_name","Thomas") 159 | ("last_name", "Cox"); 160 | 161 | auto r1_2 = mvo() 162 | ("account", "reviewer1") 163 | ("committee", "committee1") 164 | ("first_name","Thomas2") 165 | ("last_name", "Do2"); 166 | 167 | auto r2_2 = mvo() 168 | ("account", "reviewer2") 169 | ("committee", "committee2") 170 | ("first_name","Thomas2") 171 | ("last_name", "Cox2"); 172 | 173 | base_tester::push_action( N(eosio.wps), N(regreviewer), N(committee1), reviewer1_1); 174 | base_tester::push_action( N(eosio.wps), N(regreviewer), N(committee2), reviewer2_1); 175 | 176 | produce_block(); 177 | 178 | reviewer_t _r1_1; 179 | reviewer_t _r2_1; 180 | get_reviewer(_r1_1, N(reviewer1)); 181 | get_reviewer(_r2_1, N(reviewer2)); 182 | 183 | REQUIRE_MATCHING_OBJECT(_r1_1, r1_1); 184 | REQUIRE_MATCHING_OBJECT(_r2_1, r2_1); 185 | 186 | base_tester::push_action( N(eosio.wps), N(editreviewer), N(committee1), reviewer1_2); 187 | base_tester::push_action( N(eosio.wps), N(editreviewer), N(committee2), reviewer2_2); 188 | 189 | produce_block(); 190 | 191 | reviewer_t _r1_2; 192 | reviewer_t _r2_2; 193 | get_reviewer(_r1_2, N(reviewer1)); 194 | get_reviewer(_r2_2, N(reviewer2)); 195 | 196 | REQUIRE_MATCHING_OBJECT(_r1_2, r1_2); 197 | REQUIRE_MATCHING_OBJECT(_r2_2, r2_2); 198 | 199 | base_tester::push_action( N(eosio.wps), N(rmvreviewer), N(committee2), mvo()("committee","committee2")("reviewer", "reviewer2")); 200 | produce_block(); 201 | 202 | } FC_LOG_AND_RETHROW() 203 | 204 | BOOST_AUTO_TEST_SUITE_END() 205 | --------------------------------------------------------------------------------