├── .gitignore ├── examples ├── yoyow-authorize │ ├── views │ │ ├── error.ejs │ │ └── index.ejs │ ├── .gitignore │ ├── .babelrc │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── api.js │ │ └── Auth.js │ ├── conf │ │ └── config.js │ ├── lib │ │ └── utils.js │ ├── package.json │ ├── app.js │ └── bin │ │ └── www ├── discuz-plugin │ ├── yoyowauth.zip │ └── readme.md └── wordpress-plugin │ ├── yoyow-auth.zip │ └── readme.md ├── doc ├── 授权登录接口文档.docx └── yoyow授权SDK相关文档(nodejs).docx ├── middleware ├── .gitignore ├── views │ ├── index.jade │ ├── error.jade │ └── layout.jade ├── .babelrc ├── public │ ├── images │ │ ├── step1.png │ │ ├── step2.png │ │ ├── step3.png │ │ ├── step4.png │ │ ├── step1-en.png │ │ └── step3-en.png │ └── stylesheets │ │ └── style.css ├── routes │ ├── index.js │ ├── auth.js │ └── api.js ├── lib │ ├── ErrorUtils.js │ ├── aes-json-format.js │ ├── entity.js │ ├── Secure.js │ ├── utils.js │ ├── Auth.js │ └── Api.js ├── README-docker.md ├── conf │ └── config.js ├── README-docker-en.md ├── Dockerfile ├── esdoc.json ├── package.json ├── app.js ├── bin │ └── www ├── test │ ├── auth.test.js │ └── api.test.js ├── README.md └── README-EN.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_store 3 | doc/SDK-Docs 4 | node_modules -------------------------------------------------------------------------------- /examples/yoyow-authorize/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

-------------------------------------------------------------------------------- /doc/授权登录接口文档.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/doc/授权登录接口文档.docx -------------------------------------------------------------------------------- /examples/yoyow-authorize/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | npm-debug.log 4 | package-lock.json -------------------------------------------------------------------------------- /examples/yoyow-authorize/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": [] 4 | } -------------------------------------------------------------------------------- /middleware/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | npm-debug.log 4 | package-lock.json 5 | DS_store 6 | doc -------------------------------------------------------------------------------- /middleware/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /doc/yoyow授权SDK相关文档(nodejs).docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/doc/yoyow授权SDK相关文档(nodejs).docx -------------------------------------------------------------------------------- /middleware/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-2" 5 | ], 6 | "plugins": [] 7 | } -------------------------------------------------------------------------------- /middleware/public/images/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step1.png -------------------------------------------------------------------------------- /middleware/public/images/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step2.png -------------------------------------------------------------------------------- /middleware/public/images/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step3.png -------------------------------------------------------------------------------- /middleware/public/images/step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step4.png -------------------------------------------------------------------------------- /examples/discuz-plugin/yoyowauth.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/examples/discuz-plugin/yoyowauth.zip -------------------------------------------------------------------------------- /middleware/public/images/step1-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step1-en.png -------------------------------------------------------------------------------- /middleware/public/images/step3-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/middleware/public/images/step3-en.png -------------------------------------------------------------------------------- /middleware/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /examples/wordpress-plugin/yoyow-auth.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoyow-org/yoyow-node-sdk/HEAD/examples/wordpress-plugin/yoyow-auth.zip -------------------------------------------------------------------------------- /middleware/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /middleware/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /middleware/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.sendfile("../doc/index.html"); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | 5 | 6 | 7 | /* GET home page. */ 8 | router.get('/', function (req, res, next) { 9 | res.render('index', {title: 'Express'}); 10 | }); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/conf/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // api服务器地址 3 | apiServer: "ws://47.52.155.181:8090", 4 | //平台所有者零钱私钥 5 | secondary_key: "输入平台所有者零钱私钥", 6 | //平台id 7 | platform_id: "12603208", 8 | //钱包授权页URL 9 | wallet_url: "http://demo.yoyow.org:8000/#/authorize-service", 10 | //允许接入的IP列表 11 | allow_ip: ["127.0.0.1"] 12 | }; -------------------------------------------------------------------------------- /middleware/lib/ErrorUtils.js: -------------------------------------------------------------------------------- 1 | export default { 2 | formatError(e){ 3 | if(e.message.indexOf('last_post_sequence') >= 0) 4 | e = {code: 3001, message: '文章ID必须为该平台该发文人的上一篇文章ID +1'}; 5 | else if(e.message.indexOf('Missing Secondary Authority') >= 0) 6 | e = {code: 1007, message: `未授权该平台`}; 7 | else if(e.message.indexOf('less than required') >= 0) 8 | e = {code: 2004, message: `零钱和积分不足支付操作手续费`}; 9 | 10 | return e; 11 | } 12 | } -------------------------------------------------------------------------------- /examples/discuz-plugin/readme.md: -------------------------------------------------------------------------------- 1 | # Discuz-YOYOW-AUTH # 2 | 3 | Contributors: YOYOW 4 | 5 | Tags: YOYOW, auth 6 | 7 | Requires at least: X3.4 8 | 9 | Tested up to: X3.4 10 | 11 | Stable tag: 0.2 12 | 13 | ## Description ## 14 | 15 | ### Usage ### 16 | 17 | 1. git clone https://github.com/yoyow-org/yoyow-node-sdk.git 18 | 2. cd yoyow-node-sdk/examples/yoyow-authorize 19 | 3. npm install 20 | 4. edit conf/config.js 21 | 5. npm start 22 | 6. install yoyowauth.zip in discuz. 23 | 7. setting "API Server" and "Platform Id" 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yoyow-node-sdk 2 | 3 | ## The SDK includes the following: 4 | ### Serializer 5 | #### Serializer, types, ops, template, SerializerValidation 6 | ### ECC 7 | #### Address, Aes, PrivateKey, PublicKey, Signature, brainKey, hash, key 8 | ### Chain 9 | #### ChainStore, TransactionBuilder, ChainTypes, TransactionHelper, ChainValidation, AccountUtils 10 | ### Net 11 | #### Apis, ChainConfig, Manager 12 | 13 | ### Middleware Document 14 | 15 | See [Middleware](https://github.com/yoyow-org/yoyow-node-sdk/tree/master/middleware). 16 | -------------------------------------------------------------------------------- /middleware/README-docker.md: -------------------------------------------------------------------------------- 1 | # 基于 Docker 进行部署 2 | 3 | ### 拉取代码 4 | 5 | ``` 6 | $ git clone https://github.com/yoyow-org/yoyow-node-sdk.git 7 | ``` 8 | 9 | ### 修改配置文件 10 | 11 | ``` 12 | $ cd middleware 13 | $ vim conf/config.js 14 | ``` 15 | 16 | ### 打包镜像 17 | 18 | ``` 19 | $ cd middleware 20 | $ docker build -t yoyow-middleware . 21 | ``` 22 | 23 | ### 启动容器 24 | 25 | ``` 26 | $ docker run -itd --name yoyow-middleware-1 -v CONFIG_FILE_PATH:/app/conf -p 3001:3001 --restart always yoyow-middleware 27 | ``` 28 | 29 | # 已知问题 30 | 31 | * 在 MacOSX 下,容器无法获取客户端真实IP,只能获取到 docker0 的 IP 32 | -------------------------------------------------------------------------------- /examples/wordpress-plugin/readme.md: -------------------------------------------------------------------------------- 1 | # WP-YOYOW-AUTH # 2 | 3 | Contributors: YOYOW 4 | 5 | Tags: YOYOW, auth 6 | 7 | Requires at least: 4.7 8 | 9 | Tested up to: 4.7 10 | 11 | Stable tag: 0.2 12 | 13 | ## Description ## 14 | 15 | ### Usage ### 16 | 17 | 1. git clone https://github.com/yoyow-org/yoyow-node-sdk.git 18 | 2. cd yoyow-node-sdk/examples/yoyow-authorize 19 | 3. npm install 20 | 4. edit conf/config.js 21 | 5. npm start 22 | 6. install yoyow-auth.zip in wordpress. 23 | 7. setting "API Server" and "Platform Id" 24 | 8. Add code to the location you want 25 | `` -------------------------------------------------------------------------------- /examples/yoyow-authorize/lib/utils.js: -------------------------------------------------------------------------------- 1 | let utils = { 2 | isEmpty: (obj) => { 3 | return obj == undefined || obj == null || obj == 'null' || obj == '' || obj.length == 0; 4 | }, 5 | resJson: (res, code = 0, data = null, message = null) => { 6 | let obj = {code: code, data: data}; 7 | if (message != null) obj.message = message; 8 | res.json(obj); 9 | }, 10 | reqJson: (query) => { 11 | if (typeof(query) == "object") return query; 12 | if (!utils.isEmpty(query)) { 13 | return JSON.parse(query); 14 | } 15 | return {}; 16 | } 17 | }; 18 | export default utils; -------------------------------------------------------------------------------- /middleware/conf/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // api服务器地址 3 | apiServer: "ws://47.52.155.181:10011", 4 | // 请求有效时间,单位s 5 | secure_ageing: 1000, 6 | // 平台安全请求验证key 由平台自定义 7 | secure_key: "", 8 | // 平台所有者active私钥 9 | active_key: "", 10 | // 平台所有者零钱私钥 11 | secondary_key: "", 12 | // 平台所有者备注私钥 13 | memo_key: "", 14 | // 平台id(yoyow id) 15 | platform_id: "", 16 | // 转账是否使用积分 17 | use_csaf: true, 18 | // 转账是否转到余额 否则转到零钱 19 | to_balance: false, 20 | // 钱包授权页URL 21 | wallet_url: "http://demo.yoyow.org:8000/#/authorize-service", 22 | // 允许接入的IP列表 23 | allow_ip: ["localhost", "127.0.0.1"] 24 | }; -------------------------------------------------------------------------------- /middleware/README-docker-en.md: -------------------------------------------------------------------------------- 1 | # Deploy middleware by docker 2 | 3 | ### Pull the newest code 4 | 5 | ``` 6 | $ git clone https://github.com/yoyow-org/yoyow-node-sdk.git 7 | ``` 8 | 9 | ### Edit the configure 10 | 11 | ``` 12 | $ cd middleware 13 | $ vim conf/config.js 14 | ``` 15 | 16 | ### Build image 17 | 18 | ``` 19 | $ cd middleware 20 | $ docker build -t yoyow-middleware . 21 | ``` 22 | 23 | ### Run container 24 | 25 | ``` 26 | $ docker run -itd --name yoyow-middleware-1 -v CONFIG_FILE_PATH:/app/conf -p 3001:3001 --restart always yoyow-middleware 27 | ``` 28 | 29 | # Issues 30 | 31 | * In MacOSX container cannot get real remote client IP. The container will only get docker0 IP. 32 | -------------------------------------------------------------------------------- /middleware/lib/aes-json-format.js: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js'; 2 | 3 | export default { 4 | stringify: function (cipherParams) { 5 | var j = {ct: cipherParams.ciphertext.toString(CryptoJS.enc.Hex)}; 6 | if (cipherParams.iv) j.iv = cipherParams.iv.toString(); 7 | if (cipherParams.salt) j.s = cipherParams.salt.toString(); 8 | return JSON.stringify(j); 9 | }, 10 | parse: function (jsonStr) { 11 | var j = JSON.parse(jsonStr); 12 | var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(j.ct)}); 13 | if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv); 14 | if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s); 15 | return cipherParams; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yoyow-authorize", 3 | "version": "0.0.1", 4 | "engines": { 5 | "node": ">= 6.9.2" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "cross-env PORT=3000 babel-node ./bin/www" 10 | }, 11 | "dependencies": { 12 | "body-parser": "~1.18.2", 13 | "cookie-parser": "~1.4.3", 14 | "cross-env": "^5.1.1", 15 | "debug": "~2.6.9", 16 | "ejs": "~2.5.7", 17 | "express": "~4.15.5", 18 | "morgan": "~1.9.0", 19 | "serve-favicon": "~2.4.5", 20 | "ws": "^3.3.0" 21 | }, 22 | "devDependencies": { 23 | "babel-cli": "^6.24.1", 24 | "babel-loader": "^7.1.1", 25 | "babel-preset-es2015": "^6.24.1", 26 | "babel-preset-stage-2": "^6.24.1", 27 | "nodemon": "^1.11.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /middleware/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | EXPOSE 3001 4 | 5 | # install nvm and nodejs 6 | ENV NVM_DIR=/usr/local/nvm NODE_VERSION=10.1.0 7 | 8 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh &&\ 9 | apt-get update &&\ 10 | apt-get install -y curl git &&\ 11 | apt-get -y clean all &&\ 12 | curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash 13 | 14 | RUN /bin/bash -c \ 15 | "source $NVM_DIR/nvm.sh \ 16 | && nvm install $NODE_VERSION \ 17 | && nvm alias default $NODE_VERSION \ 18 | && nvm use default" 19 | 20 | ENV NODE_PATH=$NVM_DIR/v$NODE_VERSION/lib/node_modules PATH=$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH 21 | 22 | # deploy middleware 23 | WORKDIR /app 24 | COPY . /app 25 | RUN rm -rf node_modules && npm install 26 | 27 | CMD ["npm", "start"] 28 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/routes/api.js: -------------------------------------------------------------------------------- 1 | import {ChainStore} from "yoyowjs-lib"; 2 | import config from "../conf/config"; 3 | import utils from "../lib/utils"; 4 | 5 | var express = require('express'); 6 | var router = express.Router(); 7 | 8 | router.get("/getAccount", (req, res, next) => { 9 | let query = utils.reqJson(req.query); 10 | if (query["uid"]) { 11 | ChainStore.fetchAccountByUid(query.uid).then(uObj => { 12 | if (null == uObj) { 13 | utils.resJson(res, 2001, uObj, "账号不存在"); 14 | } else { 15 | utils.resJson(res, 0, uObj, "操作成功"); 16 | } 17 | }).catch(err => { 18 | utils.resJson(res, 2002, null, "操作失败:" + err.message); 19 | }); 20 | } else { 21 | utils.resJson(res, 2000, null, "参数错误"); 22 | } 23 | 24 | }); 25 | 26 | 27 | module.exports = router; -------------------------------------------------------------------------------- /middleware/esdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": ".", 3 | "includes": ["\\.(js|es6)$"], 4 | "excludes": [ 5 | "node_modules", 6 | "bin", 7 | "lib/utils.js", 8 | "lib/yoyow-node-sdk.js", 9 | "public", 10 | "routes", 11 | "views" 12 | ], 13 | "destination": "./doc", 14 | "index": "./README.md", 15 | "package": "./package.json", 16 | "plugins": [ 17 | { 18 | "name": "esdoc-standard-plugin", 19 | "option": { 20 | "lint": { 21 | "enable": true 22 | }, 23 | "coverage": { 24 | "enable": true 25 | }, 26 | "accessor": { 27 | "access": [ 28 | "public", 29 | "protected", 30 | "private" 31 | ], 32 | "autoPrivate": false 33 | }, 34 | "undocumentIdentifier": { 35 | "enable": true 36 | }, 37 | "unexportedIdentifier": { 38 | "enable": false 39 | }, 40 | "typeInference": { 41 | "enable": true 42 | }, 43 | "test": {"source": "./test/"} 44 | } 45 | }, 46 | { 47 | "name": "esdoc-ecmascript-proposal-plugin", 48 | "option": {"all": true} 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /middleware/routes/auth.js: -------------------------------------------------------------------------------- 1 | import Auth from '../lib/Auth'; 2 | import config from '../conf/config'; 3 | import utils from '../lib/utils'; 4 | import QRImage from "qr-image"; 5 | 6 | var express = require('express'); 7 | var router = express.Router(); 8 | 9 | router.get("/sign", (req, res, next) => { 10 | Auth.sign('platform', config.platform_id, config.secondary_key).then(signObj => { 11 | signObj.url = config.wallet_url; 12 | utils.success(res, signObj); 13 | }).catch(e => { 14 | utils.error(res, e); 15 | }); 16 | }); 17 | 18 | router.get("/signQR", (req, res, next) => { 19 | let {state} = req.query; 20 | 21 | Auth.sign('platform', config.platform_id, config.secondary_key).then(signObj => { 22 | signObj.state = state; 23 | let qrBuffer = QRImage.imageSync(JSON.stringify(signObj), { type: 'png' }); 24 | utils.success(res, qrBuffer.toString('base64')); 25 | }).catch(e => { 26 | utils.error(res, e); 27 | }); 28 | }); 29 | 30 | router.get('/verify', (req, res, next) => { 31 | let {yoyow, time, sign} = req.query; 32 | console.log('授权验证'); 33 | Auth.verify('yoyow', sign, time, yoyow).then(verifyObj => { 34 | utils.success(res, verifyObj); 35 | }).catch(e => { 36 | utils.error(res, e); 37 | }); 38 | }); 39 | 40 | module.exports = router; 41 | -------------------------------------------------------------------------------- /middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yoyow-middleware", 3 | "version": "0.0.1", 4 | "engines": { 5 | "node": ">= 6.9.2" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "cross-env PORT=3001 babel-node ./bin/www", 10 | "debug": "cross-env PORT=3001 nodemon ./bin/www --exec babel-node", 11 | "doc": "esdoc -c esdoc.json", 12 | "test": "cross-env mocha -t 10000 --require babel-core/register", 13 | "test:api": "cross-env mocha -t 100000 --require babel-core/register ./test/api.test", 14 | "test:auth": "cross-env mocha -t 10000 --require babel-core/register ./test/auth.test" 15 | }, 16 | "dependencies": { 17 | "body-parser": "~1.18.2", 18 | "cookie-parser": "~1.4.3", 19 | "crypto-js": "^3.1.9-1", 20 | "debug": "~2.6.9", 21 | "express": "~4.15.5", 22 | "jade": "~1.11.0", 23 | "morgan": "~1.9.0", 24 | "qr-image": "^3.2.0", 25 | "serve-favicon": "~2.4.5", 26 | "yoyowjs-lib": "git+https://github.com/yoyow-org/yoyowjs-lib.git" 27 | }, 28 | "devDependencies": { 29 | "assert": "^1.4.1", 30 | "babel-cli": "^6.24.1", 31 | "babel-loader": "^7.1.1", 32 | "babel-preset-es2015": "^6.24.1", 33 | "babel-preset-stage-2": "^6.24.1", 34 | "bytebuffer": "^5.0.1", 35 | "cross-env": "^5.1.3", 36 | "esdoc": "^1.0.4", 37 | "esdoc-ecmascript-proposal-plugin": "^1.0.0", 38 | "esdoc-standard-plugin": "^1.0.0", 39 | "mocha": "^4.1.0", 40 | "nodemon": "^1.11.0", 41 | "secure-random": "^1.1.1", 42 | "ws": "^4.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /middleware/lib/entity.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 签名对象 5 | */ 6 | export class SignObj{ 7 | constructor(sign, time, uid){ 8 | /** 9 | * 签名结果 10 | * @type {String} 11 | */ 12 | this.sign = sign; 13 | /** 14 | * 签名时间 15 | * @type {Number} 16 | */ 17 | this.time = time; 18 | /** 19 | * yoyow id 20 | * @type {Number|String} 21 | */ 22 | this.uid = uid; 23 | } 24 | } 25 | 26 | /** 27 | * 验签对象 28 | */ 29 | export class VerifyObj{ 30 | constructor(verify, name){ 31 | /** 32 | * 签名是否成功 33 | * @type {boolean} 34 | */ 35 | this.verify = verify; 36 | /** 37 | * 平台拥有者yoyow账号name 38 | * @type {String} 39 | */ 40 | this.name = name; 41 | } 42 | } 43 | 44 | /** 45 | * 分页对象 46 | */ 47 | // export class PageWrapper{ 48 | // constructor(curPage, maxPage, total, size, list){ 49 | // /** 50 | // * 当前页 51 | // * @type {Number} 52 | // */ 53 | // this.curPage = curPage; 54 | 55 | // /** 56 | // * 最大页 57 | // * @type {Number} 58 | // */ 59 | // this.maxPage = maxPage; 60 | 61 | // /** 62 | // * 总条数 63 | // * @type {Number} 64 | // */ 65 | // this.total = total; 66 | 67 | // /** 68 | // * 每页条数 69 | // * @type {Number} 70 | // */ 71 | // this.size = size; 72 | 73 | // /** 74 | // * 对象数组 75 | // * @type {Array} 76 | // */ 77 | // this.list = list; 78 | // } 79 | // } -------------------------------------------------------------------------------- /middleware/lib/Secure.js: -------------------------------------------------------------------------------- 1 | import utils from './utils'; 2 | import config from '../conf/config'; 3 | import CryptoJS from 'crypto-js'; 4 | import CryptoJSAesJson from './aes-json-format'; 5 | 6 | class Secure { 7 | constructor(){ 8 | this.validQueue = [ 9 | this.validCipher, 10 | this.validTime 11 | ]; 12 | } 13 | 14 | /** 15 | * 验证密文 16 | * @private 17 | */ 18 | validCipher(req, res, next){ 19 | let {ct, iv, s} = req.body; 20 | let cipher = {ct, iv, s}; 21 | let send = CryptoJS.AES.decrypt(JSON.stringify(cipher), config.secure_key, {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8); 22 | let isValid = true; 23 | try{ 24 | send = JSON.parse(send); 25 | isValid = send != null && typeof send === 'object'; 26 | }catch(e){ 27 | isValid = false; 28 | }finally{ 29 | if(isValid) { 30 | req.decryptedData = send; 31 | next(); 32 | } 33 | else res.json({code: 1005, message: '无效的操作签名'}); 34 | } 35 | } 36 | 37 | /** 38 | * 验证操作时间 39 | * @private 40 | */ 41 | validTime(req, res, next){ 42 | let {time} = req.decryptedData; 43 | if(!time) { 44 | res.json({code: 1004, message: '无效的操作时间'}); 45 | return; 46 | } 47 | let diff = (Date.now() - time) / 1000; 48 | if(diff > config.secure_ageing){ 49 | res.json({code: 1003, message: '请求已过期.'}); 50 | return; 51 | } 52 | next(); 53 | } 54 | } 55 | 56 | let secure = new Secure(); 57 | 58 | export default secure; -------------------------------------------------------------------------------- /middleware/lib/utils.js: -------------------------------------------------------------------------------- 1 | let utils = { 2 | base(obj, vType) { 3 | return Object.prototype.toString.call(obj) === `[object ${vType}]`; 4 | }, 5 | 6 | isArray(obj) { return this.base(obj, 'Array'); }, 7 | 8 | isFunction(obj) { return this.base(obj, 'Function'); }, 9 | 10 | isString(obj) { return this.base(obj, 'String'); }, 11 | 12 | isObject(obj) { return this.base(obj, 'Object'); }, 13 | 14 | isNumber(obj) { 15 | let n = Number(obj); 16 | return this.base(n, 'Number') && !isNaN(n); 17 | }, 18 | 19 | isEmpty(obj) { 20 | return obj == undefined || obj == null || obj == 'null' || obj == '' || obj.length == 0; 21 | }, 22 | 23 | isEmptyObject(obj){ 24 | for (var t in obj) 25 | return false; 26 | return true; 27 | }, 28 | 29 | resJson(res, code = 0, data = null, message = null) { 30 | let obj = { code: code, data: data }; 31 | if (message != null) obj.message = message; 32 | res.json(obj); 33 | }, 34 | 35 | reqJson(query) { 36 | if (typeof (query) == "object") return query; 37 | if (!utils.isEmpty(query)) { 38 | return JSON.parse(query); 39 | } 40 | return {}; 41 | }, 42 | 43 | success(res, obj) { 44 | utils.resJson(res, 0, obj, '操作成功'); 45 | }, 46 | 47 | error(res, err) { 48 | utils.resJson(res, err.code || 2000, null, err.message); 49 | }, 50 | 51 | getRealIp(req) { 52 | let real_ip = req.get("X-Real-IP") || req.get("X-Forwarded-For") || req.ip; 53 | if (real_ip === "::1") real_ip = "127.0.0.1"; 54 | return real_ip.match(/\d+/g).join('.'); 55 | }, 56 | 57 | getParams(req){ 58 | let paramsFrom ; 59 | if(!utils.isEmptyObject(req.query)) paramsFrom = req.query; 60 | if(!utils.isEmptyObject(req.body)) paramsFrom = req.body; 61 | if(!utils.isEmptyObject(req.params)) paramsFrom = req.params; 62 | return paramsFrom; 63 | } 64 | }; 65 | export default utils; -------------------------------------------------------------------------------- /examples/yoyow-authorize/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var index = require('./routes/index'); 9 | var Auth = require('./routes/Auth'); 10 | var api = require('./routes/api'); 11 | var config = require("./conf/config"); 12 | 13 | var app = express(); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'ejs'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({extended: false})); 24 | app.use(cookieParser()); 25 | app.use(express.static(path.join(__dirname, 'public'))); 26 | 27 | /** 28 | * 处理访问IP限制的中间件 29 | */ 30 | app.use((req, res, next) => { 31 | let real_ip = req.get("X-Real-IP") || req.get("X-Forwarded-For") || req.ip; 32 | if (real_ip === "::1") real_ip = "127.0.0.1"; 33 | let clientIp = real_ip.match(/\d+/g).join('.'); 34 | if (config.allow_ip.indexOf(clientIp) < 0) { 35 | var err = new Error("Forbidden"); 36 | err.status = 403; 37 | next(err); 38 | } else { 39 | next(); 40 | } 41 | }); 42 | 43 | app.use('/', index); 44 | app.use('/auth', Auth); 45 | app.use('/api/v1', api); 46 | 47 | // catch 404 and forward to error handler 48 | app.use(function (req, res, next) { 49 | var err = new Error('Not Found'); 50 | err.status = 404; 51 | next(err); 52 | }); 53 | 54 | // error handler 55 | app.use(function (err, req, res, next) { 56 | // set locals, only providing error in development 57 | res.locals.message = err.message; 58 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 59 | 60 | // render the error page 61 | res.status(err.status || 500); 62 | res.render('error'); 63 | }); 64 | 65 | module.exports = app; 66 | -------------------------------------------------------------------------------- /examples/yoyow-authorize/routes/Auth.js: -------------------------------------------------------------------------------- 1 | import {PublicKey, Signature, ChainStore, PrivateKey} from "yoyowjs-lib" 2 | import config from "../conf/config"; 3 | import utils from "../lib/utils"; 4 | 5 | var express = require('express'); 6 | var router = express.Router(); 7 | 8 | 9 | /** 10 | * 返回签名后的跳转参数 11 | */ 12 | router.get("/sign", (req, res, next) => { 13 | let key = PrivateKey.fromWif(config.secondary_key); 14 | let time = (new Date()).getTime().toString(); 15 | let sendObj = { 16 | "platform": config.platform_id, 17 | "time": time 18 | } 19 | let strObj = JSON.stringify(sendObj); 20 | let signed = Signature.sign(strObj, key); 21 | utils.resJson(res, 0, {sign: signed.toHex(), time: time, platform: config.platform_id}); 22 | }); 23 | 24 | /** 25 | * 验证用户提交的签名 26 | */ 27 | router.get('/verify', (req, res, next) => { 28 | var yoyow = req.query.yoyow; 29 | var time = req.query.time; 30 | var sign = req.query.sign; 31 | 32 | if (yoyow && time && sign) { 33 | ChainStore.fetchAccountByUid(yoyow).then(uObj => { 34 | if (uObj.secondary && uObj.secondary.key_auths && uObj.secondary.key_auths.length > 0) { 35 | let secondary = uObj.secondary.key_auths[0][0]; 36 | if (secondary == null) { 37 | utils.resJson(res, 1001, null, "无效的yoyow账号"); 38 | return; 39 | } 40 | //验证是否过期 41 | let cur = (new Date()).getTime(); 42 | let req = (new Date(parseInt(time))).getTime(); 43 | if (cur - req > 2 * 60 * 1000) {//请求时间与当前时间相关2分钟被视为过期 44 | utils.resJson(res, 1002, null, "请求已经过期"); 45 | return; 46 | } 47 | //验证签名 48 | let pars = JSON.stringify({yoyow, time}); 49 | let ePkey = PublicKey.fromPublicKeyString(secondary); 50 | let verify = Signature.fromHex(sign).verifyBuffer(new Buffer(pars), ePkey); 51 | //console.log("verify.......", verify) 52 | if (!verify) { 53 | utils.resJson(res, 1003, null, "签名验证失败"); 54 | } else { 55 | utils.resJson(res, 0, {verify: true, name: uObj.name}); 56 | } 57 | } else { 58 | utils.resJson(res, 1001, null, "无效的yoyow账号"); 59 | } 60 | }); 61 | } else { 62 | utils.resJson(res, 1000, null, "无效请求参数"); 63 | } 64 | }); 65 | 66 | module.exports = router; -------------------------------------------------------------------------------- /middleware/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var index = require('./routes/index'); 9 | var api = require('./routes/api'); 10 | var auth = require('./routes/auth'); 11 | 12 | var config = require("./conf/config"); 13 | 14 | import utils from './lib/utils'; 15 | 16 | var app = express(); 17 | 18 | // view engine setup 19 | app.set('views', path.join(__dirname, 'views')); 20 | app.set('view engine', 'jade'); 21 | 22 | // uncomment after placing your favicon in /public 23 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 24 | app.use(logger('dev')); 25 | app.use(bodyParser.json()); 26 | app.use(bodyParser.urlencoded({ extended: false })); 27 | app.use(cookieParser()); 28 | app.use(express.static(path.join(__dirname, 'public'))); 29 | app.use(express.static(path.join(__dirname, 'doc'))); 30 | 31 | // 跨域设置 32 | var protocols = ['http://', 'https://']; 33 | app.use(function (req, res, next) { 34 | let origin = req.headers.referer || req.headers.origin || false; 35 | if (origin) { 36 | if (origin.endsWith("/")) origin = origin.substr(0, origin.length - 1); 37 | for (var ao of config.allow_ip) { 38 | if (origin.startsWith(protocols[0] + ao) || origin.startsWith(protocols[1] + ao)) { 39 | res.header('Access-Control-Allow-Origin', origin); 40 | res.header('Access-Control-Allow-Methods', 'GET,POST'); 41 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 42 | res.header('Access-Control-Allow-Credentials', 'true'); 43 | } 44 | } 45 | } 46 | next(); 47 | }); 48 | 49 | /** 50 | * 处理访问IP限制的中间件 51 | */ 52 | app.use((req, res, next) => { 53 | let clientIp = utils.getRealIp(req); 54 | if (config.allow_ip.indexOf(clientIp) < 0) { 55 | var err = new Error("Forbidden"); 56 | err.status = 403; 57 | next(err); 58 | } else { 59 | next(); 60 | } 61 | }); 62 | 63 | app.use('/', index); 64 | app.use('/api/v1', api); 65 | app.use('/auth', auth); 66 | 67 | // catch 404 and forward to error handler 68 | app.use(function(req, res, next) { 69 | var err = new Error('Not Found'); 70 | err.status = 404; 71 | next(err); 72 | }); 73 | 74 | // error handler 75 | app.use(function(err, req, res, next) { 76 | // set locals, only providing error in development 77 | res.locals.message = err.message; 78 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 79 | 80 | // render the error page 81 | res.status(err.status || 500); 82 | res.render('error'); 83 | }); 84 | 85 | module.exports = app; 86 | -------------------------------------------------------------------------------- /middleware/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | import { ChainStore } from "yoyowjs-lib"; 7 | import { Apis } from "yoyowjs-ws"; 8 | 9 | var app = require('../app'); 10 | var debug = require('debug')('yoyow-middleware:server'); 11 | var http = require('http'); 12 | var config = require("../conf/config"); 13 | /** 14 | * Get port from environment and store in Express. 15 | */ 16 | 17 | var port = normalizePort(process.env.PORT || '3000'); 18 | app.set('port', port); 19 | 20 | let server = null; 21 | 22 | /** 23 | * Create API 24 | */ 25 | Apis.setRpcConnectionStatusCallback((msg) => { 26 | //open,error,closed 27 | console.log('Api status:', msg); 28 | if (msg === "closed") { 29 | Apis.reset(config.apiServer); 30 | } 31 | }); 32 | Apis.instance(config.apiServer, true).init_promise.then((result) => { 33 | console.log('Api connected......'); 34 | ChainStore.init().then(()=>{ 35 | /** 36 | * Create HTTP server. 37 | */ 38 | server = http.createServer(app); 39 | 40 | /** 41 | * Listen on provided port, on all network interfaces. 42 | */ 43 | server.listen(port); 44 | server.on('error', onError); 45 | server.on('listening', onListening); 46 | console.log('HTTP opened port:', port); 47 | }); 48 | }).catch(err => { 49 | console.log("Api init error:", err); 50 | }); 51 | 52 | /** 53 | * Normalize a port into a number, string, or false. 54 | */ 55 | 56 | function normalizePort(val) { 57 | var port = parseInt(val, 10); 58 | 59 | if (isNaN(port)) { 60 | // named pipe 61 | return val; 62 | } 63 | 64 | if (port >= 0) { 65 | // port number 66 | return port; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | /** 73 | * Event listener for HTTP server "error" event. 74 | */ 75 | 76 | function onError(error) { 77 | if (error.syscall !== 'listen') { 78 | throw error; 79 | } 80 | 81 | var bind = typeof port === 'string' 82 | ? 'Pipe ' + port 83 | : 'Port ' + port; 84 | 85 | // handle specific listen errors with friendly messages 86 | switch (error.code) { 87 | case 'EACCES': 88 | console.error(bind + ' requires elevated privileges'); 89 | process.exit(1); 90 | break; 91 | case 'EADDRINUSE': 92 | console.error(bind + ' is already in use'); 93 | process.exit(1); 94 | break; 95 | default: 96 | throw error; 97 | } 98 | } 99 | 100 | /** 101 | * Event listener for HTTP server "listening" event. 102 | */ 103 | 104 | function onListening() { 105 | var addr = server.address(); 106 | var bind = typeof addr === 'string' 107 | ? 'pipe ' + addr 108 | : 'port ' + addr.port; 109 | debug('Listening on ' + bind); 110 | } 111 | -------------------------------------------------------------------------------- /middleware/test/auth.test.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | import assert from 'assert'; 3 | import auth from '../lib/Auth' 4 | import config from '../conf/config'; 5 | import {Apis} from 'yoyowjs-ws'; 6 | 7 | let signObj ; 8 | 9 | /** 10 | * 测试 授权 11 | * @test {Auth} 12 | */ 13 | describe('Auth test', function(){ 14 | 15 | before(() => { 16 | return Apis.instance(config.apiServer, true).init_promise.then(function (result) { 17 | console.log('before Connect success'); 18 | }); 19 | }); 20 | 21 | after(() => { 22 | Apis.close(); 23 | signObj = null; 24 | }); 25 | 26 | /** 27 | * 平台签名 28 | * @test {Auth#sign} 29 | */ 30 | it('平台签名', () => { 31 | return new Promise((resolve, reject) => { 32 | auth.sign('platform', 217895094, '5Jo6fBSUwgssbyuvEP9RYSpNnrbH7sPXmJkiWCixQ8mueSQKqqc').then(signObj => { 33 | console.log('签名结果'); 34 | console.log(signObj); 35 | assert(signObj['platform'] == config.platform_id); 36 | resolve(); 37 | }).catch(e => { 38 | reject(e); 39 | }); 40 | }); 41 | }); 42 | 43 | /** 44 | * 平台验签 45 | * @test {Auth#verify} 46 | */ 47 | it('平台验签', () => { 48 | return new Promise((resolve, reject) => { 49 | auth.verify('platform', '1f226c9a767cae068148fda17f18ef685caca0bfb53c38e017a70cb1cebe8eb6a71f6ce32bf01745d17b22bea5459cd06109dd910a24a542be1f5e2e0326aa230e', 1521003258415, 217895094).then(vObj => { 50 | console.log('验签结果'); 51 | console.log(vObj); 52 | assert(vObj.verify) 53 | resolve(); 54 | }).catch(e => { 55 | reject(e); 56 | }); 57 | }); 58 | }); 59 | 60 | /** 61 | * yoyow签名 62 | * @test {Auth#sign} 63 | */ 64 | // it('yoyow签名', () => { 65 | // return new Promise((resolve, reject) => { 66 | // auth.sign('yoyow', 217895094, '5Jo6fBSUwgssbyuvEP9RYSpNnrbH7sPXmJkiWCixQ8mueSQKqqc').then(signObj => { 67 | // console.log('签名结果 ',signObj); 68 | // resolve(); 69 | // }).catch(e => { 70 | // reject(e); 71 | // }); 72 | // }) 73 | // }) 74 | 75 | /** 76 | * yoyow验签 77 | * @test {Auth#verify} 78 | */ 79 | // it('yoyow验签', () => { 80 | // return new Promise((resolve, reject) => { 81 | // auth.verify('yoyow', '1f1d65854c5262545b031dd2acd4c0b29494d8efae133520fe7b8e5c71273b48de533035f639e9577114cbc6dd9c5295f4b803b9ceadb6954ce32f9f2caf14fae2', 1520995016533, 217895094).then(vObj => { 82 | // console.log('验签结果'); 83 | // console.log(vObj); 84 | // assert(vObj.verify) 85 | // resolve(); 86 | // }).catch(e => { 87 | // reject(e); 88 | // }); 89 | // }); 90 | // }) 91 | }); -------------------------------------------------------------------------------- /examples/yoyow-authorize/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { ChainStore } from "yoyowjs-lib"; 4 | import { Apis } from "yoyowjs-ws"; 5 | 6 | /** 7 | * Module dependencies. 8 | */ 9 | 10 | var app = require('../app'); 11 | var debug = require('debug')('yoyow-authorize:server'); 12 | var http = require('http'); 13 | var config = require("../conf/config"); 14 | 15 | /** 16 | * Get port from environment and store in Express. 17 | */ 18 | 19 | var port = normalizePort(process.env.PORT || '3000'); 20 | app.set('port', port); 21 | var server = null; 22 | 23 | /** 24 | * Create API 25 | */ 26 | Apis.setRpcConnectionStatusCallback((msg) => { 27 | //open,error,closed 28 | console.log('Api status:', msg); 29 | if (msg === "closed") { 30 | Apis.reset(config.apiServer); 31 | } 32 | }); 33 | Apis.instance(config.apiServer, true).init_promise.then((result) => { 34 | console.log('Api connected......'); 35 | ChainStore.init().then(()=>{ 36 | /** 37 | * Create HTTP server. 38 | */ 39 | server = http.createServer(app); 40 | 41 | /** 42 | * Listen on provided port, on all network interfaces. 43 | */ 44 | server.listen(port); 45 | server.on('error', onError); 46 | server.on('listening', onListening); 47 | console.log('HTTP opened port:', port); 48 | }); 49 | }).catch(err => { 50 | console.log("Api init error:", err); 51 | }); 52 | 53 | 54 | /** 55 | * Normalize a port into a number, string, or false. 56 | */ 57 | 58 | function normalizePort(val) { 59 | var port = parseInt(val, 10); 60 | 61 | if (isNaN(port)) { 62 | // named pipe 63 | return val; 64 | } 65 | 66 | if (port >= 0) { 67 | // port number 68 | return port; 69 | } 70 | 71 | return false; 72 | } 73 | 74 | /** 75 | * Event listener for HTTP server "error" event. 76 | */ 77 | 78 | function onError(error) { 79 | if (error.syscall !== 'listen') { 80 | throw error; 81 | } 82 | 83 | var bind = typeof port === 'string' 84 | ? 'Pipe ' + port 85 | : 'Port ' + port; 86 | 87 | // handle specific listen errors with friendly messages 88 | switch (error.code) { 89 | case 'EACCES': 90 | console.error(bind + ' requires elevated privileges'); 91 | process.exit(1); 92 | break; 93 | case 'EADDRINUSE': 94 | console.error(bind + ' is already in use'); 95 | process.exit(1); 96 | break; 97 | default: 98 | throw error; 99 | } 100 | } 101 | 102 | /** 103 | * Event listener for HTTP server "listening" event. 104 | */ 105 | 106 | function onListening() { 107 | var addr = server.address(); 108 | var bind = typeof addr === 'string' 109 | ? 'pipe ' + addr 110 | : 'port ' + addr.port; 111 | debug('Listening on ' + bind); 112 | } 113 | -------------------------------------------------------------------------------- /middleware/lib/Auth.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | import {PublicKey, Signature, PrivateKey} from "yoyowjs-lib" 3 | import config from '../conf/config'; 4 | import {SignObj, VerifyObj} from './entity'; 5 | import api from './Api'; 6 | 7 | 8 | /** 9 | * 授权相关操作 10 | */ 11 | class Auth { 12 | constructor(){} 13 | 14 | /** 15 | * 签名 16 | * @param {String} type 签名类型 platform 或 yoyow 17 | * @param {Number|String} uid yoyow账户id 18 | * @param {String} key 零钱密钥 19 | * @returns {Promise|Promise.|*|Promise} resolve(signObj 签名对象), reject(e 异常信息) 20 | */ 21 | sign(type, uid, key){ 22 | return new Promise((resolve, reject) => { 23 | if(type != 'platform' && type != 'yoyow'){ 24 | reject({code: 1001, message: '无效的签名类型'}); 25 | } 26 | let time = Date.now().toString(); 27 | let sendObj = {}; 28 | sendObj[type] = uid; 29 | sendObj.time = time; 30 | let strObj = JSON.stringify(sendObj); 31 | let signed = Signature.sign(strObj, PrivateKey.fromWif(key)); 32 | let result = {sign: signed.toHex(), time: time}; 33 | result[type] = uid; 34 | resolve(result); 35 | }); 36 | } 37 | 38 | 39 | 40 | /** 41 | * 验证签名 42 | * @param {String} type 验签类型 platform 或 yoyow 43 | * @param {String} sign 验签对象 44 | * @param {String|Number} time 签名时间 45 | * @param {Number} uid yoyow账户id 46 | * @returns {Promise|Promise.|*|Promise} resolve(verifyObj 签名验证对象), reject(e 异常信息) 47 | * @description 私钥签名 公钥验证 48 | */ 49 | verify(type, sign, time, uid){ 50 | return new Promise((resolve, reject) => { 51 | if(typeof time === 'number') time = time.toString(); 52 | if(type != 'platform' && type != 'yoyow'){ 53 | reject({code: 1001, message: '无效的签名类型'}); 54 | }else if(isNaN(Number(time)) && Object.prototype.toString.call(time) !== '[object Number'){ 55 | reject({code: 1002, message: '无效的签名时间'}); 56 | }else{ 57 | return api.getAccount(uid).then(uObj => { 58 | let cur = (new Date()).getTime(); 59 | let req = (new Date(parseInt(time))).getTime(); 60 | if (cur - req > config.secure_ageing * 1000) {//请求时间与当前时间相关2分钟被视为过期 61 | reject({code: 1003, message: '请求已经过期'}); 62 | } else { 63 | let active = uObj.active.key_auths[0][0]; 64 | let sendObj = {}; 65 | sendObj[type] = uid; 66 | sendObj.time = time; 67 | let pars = JSON.stringify(sendObj); 68 | let ePkey = PublicKey.fromPublicKeyString(active); 69 | let verify = Signature.fromHex(sign).verifyBuffer(new Buffer(pars), ePkey); 70 | if(!verify) reject({code: 1006, message: '账号信息与链上不匹配'}); 71 | else resolve(new VerifyObj(verify, uObj.name)); 72 | } 73 | }).catch(e => { 74 | reject(e); 75 | }); 76 | } 77 | }); 78 | } 79 | } 80 | 81 | export default new Auth(); -------------------------------------------------------------------------------- /middleware/routes/api.js: -------------------------------------------------------------------------------- 1 | import Api from '../lib/Api'; 2 | import config from '../conf/config'; 3 | import utils from '../lib/utils'; 4 | import Secure from '../lib/Secure'; 5 | 6 | var express = require('express'); 7 | var router = express.Router(); 8 | 9 | router.get('/getAccount', (req, res, next) => { 10 | let {uid} = req.query; 11 | Api.getAccount(uid).then(uObj => { 12 | utils.success(res, uObj); 13 | }).catch(e => { 14 | utils.error(res, e); 15 | }); 16 | }); 17 | 18 | router.post('/transfer', Secure.validQueue, (req, res, next) => { 19 | let {uid, amount, asset_id, memo} = req.decryptedData; 20 | let key = config.secondary_key; 21 | if(asset_id && asset_id != 0){ 22 | key = config.active_key; 23 | }else{ 24 | asset_id = 0; 25 | } 26 | Api.transfer(config.platform_id, asset_id, key, uid, amount, config.use_csaf, config.to_balance, memo, config.memo_key).then(tx => { 27 | utils.success(res, tx); 28 | }).catch(e => { 29 | console.log(e); 30 | utils.error(res, e); 31 | }); 32 | }); 33 | 34 | router.get('/getQRReceive', (req, res, next) => { 35 | let {amount, memo, asset} = req.query; 36 | Api.getQRReceive(config.platform_id, amount, memo, asset) 37 | .then(str => utils.success(res, str)) 38 | .catch(err => utils.error(res, err)); 39 | }) 40 | 41 | router.get('/getHistory', (req, res, next) => { 42 | let {uid, op_type, start, limit} = req.query; 43 | Api.getHistory(uid, op_type, start, limit).then(data => { 44 | utils.success(res, data); 45 | }).catch(e => { 46 | utils.error(res, e); 47 | }); 48 | }); 49 | 50 | router.get('/confirmBlock', (req, res, next) => { 51 | let {block_num} = req.query; 52 | Api.confirmBlock(block_num).then(bool => { 53 | utils.success(res, bool); 54 | }).catch(e => { 55 | utils.error(res, e); 56 | }); 57 | }); 58 | 59 | router.post('/post', Secure.validQueue, (req, res, next) => { 60 | let {platform, poster, post_pid, title, body, extra_data, origin_platform, origin_poster, origin_post_pid} = req.decryptedData; 61 | Api.post(platform, poster, post_pid, title, body, extra_data, origin_platform, origin_poster, origin_post_pid).then( tx => { 62 | utils.success(res, tx); 63 | }).catch(e => { 64 | utils.error(res, e); 65 | }); 66 | }); 67 | 68 | router.post('/postUpdate', Secure.validQueue, (req, res, next) => { 69 | let {platform, poster, post_pid, title, body, extra_data} = req.decryptedData; 70 | Api.postUpdate(platform, poster, post_pid, title, body, extra_data).then(tx => { 71 | utils.success(res, tx); 72 | }).catch(e => { 73 | utils.error(res, e); 74 | }); 75 | }); 76 | 77 | router.get('/getPost', (req, res, next) => { 78 | let {platform, poster, post_pid} = req.query; 79 | Api.getPost(platform, poster, post_pid).then(post => { 80 | utils.success(res, post); 81 | }).catch(e => { 82 | utils.error(res, e); 83 | }); 84 | }); 85 | 86 | router.get('/getPostList', (req, res, next) => { 87 | let {platform, poster, limit, start} = req.query; 88 | Api.getPostList(platform, poster, limit, start).then(list => { 89 | utils.success(res, list); 90 | }).catch(e => { 91 | utils.error(res, e); 92 | }); 93 | }); 94 | 95 | router.post('/updateAllowedAssets', (req, res, next) => { 96 | let {uid, asset_id} = req.decryptedData; 97 | Api.updateAllowedAssets(uid, asset_id).then(tx => { 98 | utils.success(res, tx); 99 | }).catch(e => { 100 | utils.error(res, e); 101 | }) 102 | }); 103 | 104 | router.get('/getAsset', (req, res, next) => { 105 | let {search} = req.query; 106 | Api.getAsset(search).then(asset => { 107 | utils.success(res, asset); 108 | }).catch(e => { 109 | utils.error(res, e); 110 | }) 111 | }); 112 | 113 | router.get('/getPlatformById', (req, res, next) => { 114 | let {uid} = req.query; 115 | Api.getPlatformById(uid).then(platform => { 116 | utils.success(res, platform); 117 | }).catch(e => { 118 | utils.error(res, e); 119 | }) 120 | }); 121 | 122 | module.exports = router; 123 | -------------------------------------------------------------------------------- /middleware/test/api.test.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | import assert from 'assert'; 3 | import config from '../conf/config'; 4 | import api from '../lib/api'; 5 | import {ChainStore} from "yoyowjs-lib"; 6 | import {Apis} from "yoyowjs-ws"; 7 | 8 | /** 9 | * 测试 api 10 | * @test {Api} 11 | */ 12 | describe('Api test', () => { 13 | before(function() { 14 | return Apis.instance(config.apiServer, true).init_promise.then((result) => { 15 | console.log('before Connect success'); 16 | ChainStore.init(); 17 | }); 18 | }); 19 | 20 | after(() => { 21 | Apis.close(); 22 | }); 23 | 24 | // /** 25 | // * 获取账户信息 26 | // * @test {Api#getAccount} 27 | // */ 28 | // it('获取账户信息', () => { 29 | // return new Promise((resolve, reject) => { 30 | // api.getAccount(config.platform_id).then(obj => { 31 | // assert(obj.uid == config.platform_id) 32 | // resolve(); 33 | // }).catch(e => { 34 | // reject(e); 35 | // }); 36 | // }); 37 | // }); 38 | 39 | // /** 40 | // * 转账 41 | // * @test {Api#transfer} 42 | // */ 43 | // it('测试转账', () => { 44 | // return new Promise((resolve, reject) => { 45 | // let now = new Date(); 46 | // api.transfer(config.platform_id, 10 ,config.active_key, 221970467, 0.0001, false, true, 'hello yoyow '+now.toISOString(), config.memo_key).then(block_num => { 47 | // console.log('交易块号'); 48 | // console.log(block_num); 49 | // resolve(); 50 | // }).catch(e => { 51 | // reject(e); 52 | // }); 53 | // }); 54 | // }) 55 | 56 | // /** 57 | // * 获取账户历史记录 58 | // * @test {Api#getHistory} 59 | // */ 60 | // it('获取账户历史记录', () => { 61 | // return new Promise((resolve, reject) => { 62 | // api.getHistory(config.platform_id, 1, 10).then(pw => { 63 | // console.log(pw); 64 | // resolve(); 65 | // }).catch(e => { 66 | // reject(e); 67 | // }); 68 | // }); 69 | // }); 70 | 71 | // /** 72 | // * 验证当前块是否不可退回 73 | // * @test {Api#confirmBlock} 74 | // */ 75 | // it('验证当前块是否不可退回', () => { 76 | // return new Promise((resolve, reject) => { 77 | // let block_num = 4271077; 78 | // api.confirmBlock(block_num).then(flag => { 79 | // console.log(`验证块 ${block_num} 是否不可退回 : ${flag}`); 80 | // resolve(); 81 | // }).catch(e => { 82 | // reject(e); 83 | // }) 84 | // }); 85 | // }); 86 | 87 | // it('test post', () => { 88 | // return new Promise((resolve, reject) => { 89 | // api.post(217895094, 210425155, 4, `title${Date.now()}`, `body ${new Date().toLocaleDateString()}`, '{a:"im zf"}').then(block_num => { 90 | // console.log('发文成功 block_num : '+block_num); 91 | // resolve(); 92 | // }).catch(e => { 93 | // reject(e); 94 | // }) 95 | // }); 96 | // }); 97 | 98 | // it('test post update', () => { 99 | // return new Promise((resolve, reject) => { 100 | // api.postUpdate(217895094, 210425155, 1, 'update title xxxxx').then(block_num => { 101 | // console.log('修改成功 block_num : '+block_num); 102 | // resolve(); 103 | // }).catch(e => { 104 | // reject(e); 105 | // }); 106 | // }); 107 | // }); 108 | 109 | // it('get post list', () => { 110 | // return new Promise((resolve, reject) => { 111 | // api.getPostList(217895094).then(res => { 112 | // console.log(res); 113 | // resolve(res); 114 | // }).catch(e => { 115 | // reject(e); 116 | // }); 117 | // }); 118 | // }); 119 | 120 | // it('get post by platform and poster', () => { 121 | // return new Promise((resolve, reject) => { 122 | // api.getPost(217895094, 25638, 5).then(res => { 123 | // console.log(res); 124 | // resolve(); 125 | // }).catch(e => { 126 | // reject(e); 127 | // }); 128 | // }); 129 | // }); 130 | 131 | // it('update account assets whitelist', () => { 132 | // return new Promise((resolve, reject) => { 133 | // api.updateAllowedAssets(9638251, 155).then(res => { 134 | // console.log(res); 135 | // resolve(); 136 | // }).catch(err => reject(err)); 137 | // }); 138 | // }); 139 | 140 | 141 | }); 142 | 143 | 144 | /** 145 | 146 | 210425155 147 | active YYW72ZYtzkFbCDDX6XhgyjPngo9MipKbhfbnSVbjkbua6ZVv4MR1B 5KHiiQs8V7q6SCa6NRnibHcAd32mSn953PKcC88aXBxbvEfKLSP 148 | secondary YYW7gEvymRQZmeNEjsNE4BggZqd5CXszqoPAQDT5hTbSNWkUZTZN7 5KZpNkZGZdJWTQ7Y4m2sf4yEx7EAXwkRLPjTKS9W7bRhMLhM2LU 149 | memo YYW6AgNqiRHoaNC3YsdH4r43tG9yS717D9CxpgQ88rQPztW13BpFf 5HuahHraSyuBwAoMgHVWqtgCdNuV7eSq1qEcqRLZgbn33pHkkdQ 150 | 151 | */ -------------------------------------------------------------------------------- /middleware/lib/Api.js: -------------------------------------------------------------------------------- 1 | import { ChainStore, ChainTypes, PrivateKey, AccountUtils, Aes, TransactionBuilder, hash } from "yoyowjs-lib"; 2 | import { Apis } from "yoyowjs-ws"; 3 | import config from "../conf/config"; 4 | import secureRandom from 'secure-random'; 5 | import { Long } from 'bytebuffer'; 6 | import { PageWrapper } from './entity'; 7 | import utils from './utils'; 8 | import ErrorUtils from './ErrorUtils'; 9 | 10 | let dynamic_global_params_type = `2.${parseInt(ChainTypes.impl_object_type.dynamic_global_property, 10)}.0`; 11 | 12 | let _getAssetPrecision = (precision) => { 13 | return Math.pow(10, precision); 14 | } 15 | 16 | /** 17 | * Api 操作 18 | */ 19 | class Api { 20 | constructor() { } 21 | 22 | /** 23 | * 获取账户信息 24 | * @param {Number|String} uid yoyow账号 25 | * @returns {Promise|Promise.|*|Promise} resolve(uObj yoyow用户对象), reject(e 异常信息) 26 | */ 27 | getAccount(uid) { 28 | return new Promise((resolve, reject) => { 29 | if (AccountUtils.validAccountUID(uid)) { 30 | ChainStore.fetchAccountByUid(uid).then(uObj => { 31 | if (null == uObj) { 32 | reject({code: 2001, message: '账号不存在'}); 33 | }else{ 34 | Promise.all([ 35 | ChainStore.fetchAccountStatisticsByUid(uid), 36 | ChainStore.fetchAccountBalances(uid, []) 37 | ]).then(res => { 38 | let [statistics, assets] = res; 39 | uObj.statistics = { 40 | obj_id: statistics.id, 41 | core_balance: statistics.core_balance, 42 | csaf: statistics.csaf, 43 | prepaid: statistics.prepaid, 44 | total_witness_pledge: statistics.total_witness_pledge, 45 | total_committee_member_pledge: statistics.total_committee_member_pledge, 46 | total_platform_pledge: statistics.total_platform_pledge, 47 | releasing_witness_pledge: statistics.releasing_witness_pledge, 48 | releasing_committee_member_pledge: statistics.releasing_committee_member_pledge, 49 | releasing_platform_pledge: statistics.releasing_platform_pledge 50 | } 51 | uObj.assets = assets; 52 | resolve(uObj); 53 | }).catch(e => { 54 | reject(e); 55 | }); 56 | } 57 | }).catch(err => { 58 | reject(err); 59 | }); 60 | } else { 61 | reject({code: 2002, message: '无效的账号'}); 62 | } 63 | }); 64 | } 65 | 66 | /** 67 | * 转账 68 | * @param {Number|String} from_uid 转出yoyow账号 69 | * @param {Number} asset_id 资产id 70 | * @param {String} from_key 转出账号零钱私钥 71 | * @param {Number|String} to_uid 转入yoyow账号 72 | * @param {Number} amount 转账数额 73 | * @param {boolean} [use_csaf = true] 是否使用积分 - 默认为 true 74 | * @param {boolean} [toBlance = true] 是否转账到零钱 75 | * @param {String} [memo] 转账备注 76 | * @param {String} [memo_key] 备注密钥 - 需要写入备注时必填 77 | * @returns {Promise|Promise.|*|Promise} resolve({block_num 操作所属块号, txid 操作id}), reject(e 异常信息) 78 | */ 79 | transfer(from_uid, asset_id, from_key, to_uid, amount, use_csaf = true, toBlance = true, memo, memo_key) { 80 | let fetchFromKey = new Promise((resolve, reject) => { 81 | return this.getAccount(from_uid).then(uObj => { 82 | resolve(uObj); 83 | }).catch(e => { 84 | reject(e); 85 | }); 86 | }); 87 | 88 | let fetchToKey = new Promise((resolve, reject) => { 89 | return this.getAccount(to_uid).then(uObj => { 90 | resolve(uObj); 91 | }).catch(e => { 92 | reject(e); 93 | });; 94 | }); 95 | 96 | let fetchAsset = new Promise((resolve, reject) => { 97 | return this.getAsset(asset_id).then(asset => { 98 | resolve(asset); 99 | }).catch(e => { 100 | reject(e); 101 | }) 102 | }); 103 | 104 | return new Promise((resolve, reject) => { 105 | if (!utils.isNumber(amount)) { 106 | reject({code: 2003, message: '无效的转账金额'}); 107 | } else if (memo && !memo_key) { 108 | reject({code: 2004, message: '无效的备注私钥'}); 109 | } else { 110 | Promise.all([fetchFromKey, fetchToKey, fetchAsset]).then(res => { 111 | let memoFromKey = res[0].memo_key; 112 | let memoToKey = res[1].memo_key; 113 | let retain_count = _getAssetPrecision(res[2].precision); //资产精度参数 114 | let asset = { amount: Math.round(amount * retain_count), asset_id: asset_id }; 115 | let extensions_data = { 116 | from_prepaid: asset, 117 | to_balance: asset 118 | } 119 | if(!toBlance){ 120 | extensions_data = { 121 | from_prepaid: asset, 122 | to_prepaid: asset 123 | } 124 | } 125 | 126 | let op_data = { 127 | from: from_uid, 128 | to: to_uid, 129 | amount: asset 130 | }; 131 | if(asset_id == 0) op_data.extensions = extensions_data; 132 | 133 | if (memo && memo.trim() != '') { 134 | 135 | let entropy = parseInt(secureRandom.randomUint8Array(1)[0]); 136 | var long = Long.fromNumber(Date.now()); 137 | let nonce = long.shiftLeft(8).or(Long.fromNumber(entropy)).toString(); 138 | 139 | // TODO: 用户之间通过平台转账操作,不做签名,因为平台无法获取到用户私钥 140 | let message = config.platform_id == from_uid ? Aes.encrypt_with_checksum( 141 | PrivateKey.fromWif(memo_key), 142 | memoToKey, 143 | nonce, 144 | new Buffer(memo, 'utf-8') 145 | ):new Buffer('uncrypto'+memo, 'utf-8').toString('hex'); 146 | let memo_data = { 147 | from: memoFromKey, 148 | to: memoToKey, 149 | nonce, 150 | message: message 151 | }; 152 | 153 | op_data.memo = memo_data; 154 | } 155 | 156 | let tr = new TransactionBuilder(); 157 | tr.add_type_operation('transfer', op_data); 158 | return tr.set_required_fees(from_uid, false, use_csaf).then(() => { 159 | tr.add_signer(PrivateKey.fromWif(from_key)); 160 | this.__broadCast(tr).then(res => resolve(res)).catch(err => reject(err)); 161 | }).catch(e => { 162 | reject(e); 163 | }); 164 | }).catch(e => { 165 | reject(e); 166 | }); 167 | } 168 | }); 169 | } 170 | 171 | /** 172 | * 获取账户操作历史 173 | * @param {Number} uid yoyow账户id 174 | * @param {Number} op_type 查询op类型 '0' 为 转账op,默认为null 即查询所有OP类型 175 | * @param {Number} start 查询开始编号,为0时则从最新记录开始查询,默认为0 176 | * @param {Number} limit 查询长度,最大不可超过100条,默认为10 177 | * @returns {Promise|Promise.|*|Promise} resolve(Array 历史记录对象数组), reject(e 异常信息) 178 | */ 179 | getHistory(uid, op_type = null, start = 0, limit = 10) { 180 | return this.getAccount(uid).then(uObj => { 181 | return ChainStore.fetchRelativeAccountHistory(uid, op_type, 0, limit, start).then(res => { 182 | return res; 183 | }).catch(e => { 184 | return Promise.reject(e); 185 | }); 186 | }).catch(e => { 187 | return Promise.reject(e); 188 | }); 189 | } 190 | 191 | /** 192 | * 验证块是否为不可退回 193 | * - 如 将交易所属块号传入,以验证次交易为不可退回 194 | * @param {Number} block_num 查询交易所属块号 195 | * @returns {Promise|Promise.|*|Promise} resolve(bool 是否不可退回), reject(e 异常信息) 196 | */ 197 | confirmBlock(block_num) { 198 | return Apis.instance().db_api().exec("get_objects", [[dynamic_global_params_type]]).then(global_params => { 199 | let irreversible_block_num = global_params[0].last_irreversible_block_num; 200 | return block_num <= irreversible_block_num; 201 | }).catch(e => { 202 | return Promise.reject(e); 203 | }); 204 | } 205 | 206 | /** 207 | * 发文章 208 | * - 若属于转发文章 则需传入转发参数 209 | * @param {Number} platform 平台 yoyow 账号 210 | * @param {Number} poster 发帖人 yoyow 账号 211 | * @param {Number} post_pid 文章编号 由平台管理和提供 212 | * @param {String} title 文章标题 213 | * @param {String} body 文章内容 214 | * @param {String} extra_data 拓展信息 JSON 字符串 215 | * @param {Number} [origin_platform = null] 原文章发文平台 216 | * @param {Number} [origin_poster = null] 原文章发文人 217 | * @param {Number} [origin_post_pid = null] 原文章编号 218 | * @returns {Promise|Promise.|*|Promise} resolve(block_num 操作所属块号, txid 操作id), reject(e 异常信息) 219 | */ 220 | post(platform, poster, post_pid, title, body, extra_data, origin_platform = null, origin_poster = null, origin_post_pid = null){ 221 | return new Promise((resolve, reject) => { 222 | let op_data = { 223 | post_pid: post_pid, 224 | platform: platform, 225 | poster: poster, 226 | hash_value: hash.sha256(body, 'hex').toString(), 227 | extra_data: extra_data, 228 | title: title, 229 | body: body 230 | }; 231 | // 转发情况写入转发参数 232 | if(origin_post_pid && origin_platform && origin_poster){ 233 | op_data.origin_post_pid = origin_post_pid; 234 | op_data.origin_platform = origin_platform; 235 | op_data.origin_poster = origin_poster; 236 | } 237 | 238 | let tr = new TransactionBuilder(); 239 | tr.add_type_operation('post', op_data); 240 | tr.set_required_fees(poster, false, true).then(() => { 241 | tr.add_signer(PrivateKey.fromWif(config.secondary_key)); 242 | this.__broadCast(tr).then(res => resolve(res)).catch(err => reject(err)); 243 | }).catch(e => { 244 | reject({code: 2000, message: e.message}); 245 | }); 246 | }); 247 | 248 | } 249 | 250 | /** 251 | * 更新文章 252 | * - title body extra_data 参数至少有一个不为空 253 | * @param {Number} platform 平台 yoyow 账号 254 | * @param {Number} poster 发帖人 yoyow 账号 255 | * @param {Number} post_pid 文章编号 由平台管理和提供 256 | * @param {String} [title = null] 文章标题 257 | * @param {String} [body = null] 文章内容 258 | * @param {String} [extra_data = null] 拓展信息 JSON 字符串 259 | * @returns {Promise|Promise.|*|Promise} resolve(block_num 操作所属块号, txid 操作id), reject(e 异常信息) 260 | */ 261 | postUpdate(platform, poster, post_pid, title = null, body = null, extra_data = null){ 262 | return new Promise((resolve, reject) => { 263 | let op_data = { 264 | post_pid: post_pid, 265 | platform: platform, 266 | poster: poster 267 | }; 268 | 269 | if(title) op_data.title = title; 270 | 271 | if(body){ 272 | op_data.body = body; 273 | op_data.hash_value = hash.sha256(body, 'hex').toString(); 274 | } 275 | 276 | if(extra_data) op_data.extra_data = extra_data; 277 | 278 | let tr = new TransactionBuilder(); 279 | tr.add_type_operation('post_update', op_data); 280 | tr.set_required_fees(poster, false, true).then(() => { 281 | tr.add_signer(PrivateKey.fromWif(config.secondary_key)); 282 | this.__broadCast(tr).then(res => resolve(res)).catch(err => reject(err)); 283 | }).catch(e => { 284 | reject({code: 2000, message: e.message}); 285 | }); 286 | }); 287 | } 288 | 289 | /** 290 | * 获取单个文章 291 | * @param {Number} platform 平台 yoyow 账号 292 | * @param {Number} poster 发文人 yoyow 账号 293 | * @param {Number} post_pid 文章编号 294 | * @returns {Promise|Promise.|*|Promise} resolve(post 文章对象), reject(e 异常信息) 295 | */ 296 | getPost(platform, poster, post_pid){ 297 | return new Promise((resolve, reject) => { 298 | Apis.instance().db_api().exec("get_post", [platform, poster, post_pid]).then(post => { 299 | resolve(post); 300 | }).catch(e => { 301 | reject(e); 302 | }); 303 | }); 304 | } 305 | 306 | /** 307 | * 根据平台和发帖人获取文章列表 308 | * 首次加载开始时间不传 309 | * 其他次加载,将上次数据的最后一条的create_time传入 310 | * limit 最小 1 最大 99 311 | * @param {Number} platform 平台 yoyow 账号 312 | * @param {Number} [poster = null] 发文人 yoyow 账号 313 | * @param {Number} limit 查询条数 314 | * @param {String} start 开始时间 - 'yyyy-MM-ddThh:mm:ss' ISOString 315 | * @returns {Promise|Promise.|*|Promise} resolve(list 文章列表), reject(e 异常信息) 316 | */ 317 | getPostList(platform, poster = null, limit = 20, start = new Date().toISOString().split('.')[0]){ 318 | if(limit < 0) limit = 0; 319 | if(limit > 99) limit = 99; 320 | limit ++; 321 | let flag = false; //开始时间是否为某一文章的创建时间(非首次加载情况) 322 | return new Promise((resolve, reject) => { 323 | Apis.instance().db_api().exec("get_posts_by_platform_poster", [platform, poster, [start, '1970-01-01T00:00:00'], limit]).then(posts => { 324 | posts.map(item => { 325 | if(item.create_time == start) flag = true; 326 | }); 327 | if(flag) posts.shift(); 328 | else posts.pop(); 329 | resolve(posts); 330 | }).catch(e => { 331 | reject(e); 332 | }); 333 | }); 334 | } 335 | 336 | /** 337 | * 添加资产到用户资产白名单中 338 | * @param {Number} uid - 目标账户id 339 | * @param {Number} asset_id - 资产id 340 | * @returns {Promise|Promise.|*|Promise} resolve({block_num 操作所属块号, txid 操作id}), reject(e 异常信息) 341 | */ 342 | updateAllowedAssets(uid, asset_id){ 343 | let op_data = { 344 | account: uid, 345 | assets_to_add: [asset_id], 346 | assets_to_remove: [] 347 | }; 348 | let tr = new TransactionBuilder(); 349 | tr.add_type_operation('account_update_allowed_assets', op_data); 350 | return tr.set_required_fees(uid, false, true).then(() => { 351 | tr.add_signer(PrivateKey.fromWif(config.secondary_key)); 352 | return this.__broadCast(tr); 353 | }) 354 | } 355 | 356 | /** 357 | * 获取资产信息 358 | * @param {String | Number} search - 资产符号(大写) 或 资产id 359 | * @returns {Promise|Promise.|*|Promise} resolve(asset 资产对象), reject(e 异常信息) 360 | */ 361 | getAsset(search){ 362 | return ChainStore.fetchAsset(search).then(asset => { 363 | if(asset) return asset; 364 | else return Promise.reject({code: 2006, message: '无效的资产符号或id'}); 365 | }); 366 | } 367 | 368 | /** 369 | * 获取平台信息 370 | * @param {Number} uid - 平台所有者账号uid 371 | * @returns {Promise|Promise.|*|Promise} resolve(platform 平台对象), reject(e 异常信息) 372 | */ 373 | getPlatformById(uid){ 374 | return ChainStore.fetchPlatformByUid(uid); 375 | } 376 | 377 | /** 378 | * 获取转账二维码 379 | * @param {String | Number} toAccount - 平台所有者账号uid 380 | * @param {Number} amount - 转账金额 381 | * @param {String} memo - 转账备注 382 | * @param {String | Number} asset - 转账资产符号 或 资产id 383 | */ 384 | getQRReceive(toAccount, amount, memo, asset){ 385 | let canMemo = true; 386 | if(utils.isNumber(amount) && amount >= 0 && !utils.isEmpty(memo)) 387 | canMemo = false; 388 | else{ 389 | amount = 0; 390 | memo = ''; 391 | } 392 | return this.getAsset(asset).then(a => { 393 | let {asset_id, precision, symbol} = a; 394 | let resultObj = { 395 | type: 'transfer-for-fix', 396 | toAccount, 397 | amount, 398 | memoText: memo, 399 | canMemo, 400 | transferBalance: true, 401 | tokenInfo: asset_id == 0 ? null : 402 | { asset_id, precision, symbol } 403 | } 404 | return JSON.stringify(resultObj); 405 | }); 406 | } 407 | 408 | /** 409 | * 统一广播处理 410 | * @param {TransactionBuilder} tr 411 | */ 412 | __broadCast(tr){ 413 | return new Promise((resolve, reject) => { 414 | 415 | let common_return = trx => { 416 | return { 417 | block_num: trx.head_block_number(), 418 | txid: trx.id() 419 | }; 420 | } 421 | 422 | return tr.broadcast(() => resolve(common_return(tr))) 423 | .then(() => resolve(common_return(tr))) 424 | .catch(e => { 425 | if(e.message && e.message.indexOf('Insufficient Prepaid') >= 0) 426 | e = {code: 2005, message: '零钱不足'} 427 | reject(e); 428 | }); 429 | }) 430 | } 431 | 432 | 433 | } 434 | 435 | export default new Api(); -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # yoyow-middleware 2 | 3 | ### 开始 4 | 5 | #### 1. 创建测试网账号 6 | 7 | 测试网地址 [http://demo.yoyow.org:8000](http://demo.yoyow.org:8000 "yoyow钱包测试网"). 8 | 9 | 测试网CLI下载 [https://github.com/yoyow-org/yoyow-core-testnet/releases/](https://github.com/yoyow-org/yoyow-core-testnet/releases/). 10 | 11 | ![创建测试网账号](https://github.com/bulangnisi/yoyow-node-sdk/blob/master/middleware/public/images/step1.png) 12 | 13 | 平台所有者的各权限私钥获取方式 登录钱包 》 左侧菜单设置 》 账号 》 查看权限 》 在对应权限密钥的右侧点击显示私钥 》 输入密码显示私钥 》 将看到的私钥拷贝进配置中. 14 | 15 | ![获取对应私钥](https://github.com/bulangnisi/yoyow-node-sdk/blob/master/middleware/public/images/step3.png) 16 | 17 | #### 2. 创建平台 18 | 19 | 创建平台商需要最少 11000 YOYO,其中10000 为最低抵押押金,1000为创建平台手续费(测试网络注册赠送12000 测试币) 20 | 21 | ##### 2.1 启动cli钱包 22 | ###### 2.1.1 带参数启动 23 | 24 | Ubuntu 25 | 26 | ./yoyow_client -s ws://47.52.155.181:10011 --chain-id 3505e367fe6cde243f2a1c39bd8e58557e23271dd6cbf4b29a8dc8c44c9af8fe 27 | 28 | 如若提示权限不足 29 | 30 | sudo chmod a+x * 31 | 32 | 33 | 34 | ###### 2.1.2 以配置文件启动 35 | 36 | cli 钱包 同路径下创建wallet.json 文件 37 | 38 | 写入 39 | 40 | { 41 | "chain_id": "3505e367fe6cde243f2a1c39bd8e58557e23271dd6cbf4b29a8dc8c44c9af8fe", 42 | "pending_account_registrations": [], 43 | "pending_witness_registrations": [], 44 | "labeled_keys": [], 45 | "blind_receipts": [], 46 | "ws_server": "ws://47.52.155.181:10011", 47 | "ws_user": "", 48 | "ws_password": "" 49 | } 50 | 51 | Ubuntu 52 | 53 | ./yoyow_client 54 | 55 | ##### 2.2 设置钱包密码 56 | 57 | 连接成功出现 58 | 59 | Please use the set_password method to initialize a new wallet before continuing 60 | 61 | new >>> 62 | 63 | 执行 64 | 65 | new >>> set_password 你的密码 66 | 67 | 返回 68 | 69 | set_password 你的密码 70 | null 71 | locked >>> 72 | 73 | 执行 74 | 75 | locked >>> unlock 你的密码 76 | 77 | 返回 78 | 79 | unlock 123 80 | null 81 | unlocked >>> 82 | 83 | 表示解锁成功 84 | 85 | ##### 2.3 导入资金私钥 86 | 87 | unlocked >>> import_key yoyow账号uid 资金密钥 88 | 89 | 例: 90 | 91 | unlocked >>> import_key 120252179 5JwREzpwb62iEcD6J6WXs2fbn1aSKWQWvGLNCqAEYwS31EHD7i4 92 | 93 | 返回 94 | 95 | 1937037ms th_a wallet.cpp:820 save_wallet_file ] saving wallet to file wallet.json 96 | true 97 | 98 | 如果没返回true,请检查你的uid和私钥是否正确 99 | 100 | ##### 2.4 创建平台 101 | 102 | unlocked >>> create_platform yoyow账号uid "平台名称" 抵押金额 货币符号 "平台url地址" "平台拓展信息json字符串" true 103 | 104 | 例: 105 | 106 | unlocked >>> create_platform 235145448 "myPlatform" 10000 YOYO "www.example.com" "{}" true 107 | 108 | 返回 109 | 110 | { 111 | "ref_block_num": 33094, 112 | "ref_block_prefix": 2124691028, 113 | "expiration": "2018-02-07T08:40:30", 114 | "operations": [[ 115 | 20,{ 116 | "fee": { 117 | "total": { 118 | "amount": 1029296, 119 | "asset_id": 0 120 | }, 121 | "options": { 122 | "from_csaf": { 123 | "amount": 1029296, 124 | "asset_id": 0 125 | } 126 | } 127 | }, 128 | "account": 235145448, 129 | "pledge": { 130 | "amount": 1000000000, 131 | "asset_id": 0 132 | }, 133 | "name": "myPlatform", 134 | "url": "www.example.com", 135 | "extra_data": "{}" 136 | } 137 | ] 138 | ], 139 | "signatures": [ 140 | "1f08b704dd5ccf7e05e5dec45b06ad41e6382f5dd528e3f644d52ff4fb29c2040507544d5e94b84d77d70edcd68bb35b0cded0db87816ae64979ba98eeb641d5d7" 141 | ] 142 | } 143 | 144 | ##### 2.5 更新平台 145 | 146 | unlocked >>> update_platform yoyow账号uid "平台名称" 抵押金额 货币符号 "平台url地址" "平台拓展信息json字符串" true 147 | 148 | 例: 149 | 150 | unlocked >>> update_platform 235145448 "newplatformname" 10000 YOYO null null true 151 | 152 | 返回与创建平台一样 153 | 154 | 平台名称、平台url地址和平台拓展信息如没有变动则填入null,如示例操作,不会改变平台url地址和拓展信息 155 | 156 | ##### 2.6 平台拓展信息协议 157 | 158 | 平台属性 extra_data 拓展信息 JSON对象格式字符串 中 159 | 160 | { 161 | 162 | "login":"http://example/login" 平台扫码登录请求接口 163 | 164 | "description":"平台说明" 平台描述 165 | 166 | "image":"http://example.image.jpg" 平台头像,yoyow app 1.1 中,显示的平台头像 167 | 168 | "h5url":"http://exampleH5.com" 平台h5地址,用于在无app可跳转动情况下,调整h5页面 169 | 170 | "packagename":"com.example.app" 平台android 跳转 171 | 172 | "urlscheme":"example://" 平台ios跳转 173 | 174 | } 175 | 176 | ##### 2.7 平台扫码登录 177 | 178 | App扫码授权登录将访问 平台拓展信息的 平台扫码登录请求接口 ,发送回用户签名对象 179 | 180 | { 181 | 182 | {Number} yoyow - 当前操作用户账号id 183 | 184 | {String} time - 签名时间戳字符串 185 | 186 | {String} sign - 签名字符串 187 | 188 | {String} state - 平台签名时传入的自定义信息 (参考 Auth 相关 2.3 - signQR) 189 | 190 | } 191 | 192 | 约定 平台提供的接口必须返回以下信息 193 | 194 | { 195 | 196 | {Number} code - 操作结果 0 为通过 任何非 0 情况视为错误处理 197 | 198 | {String} message - 操作结果描述 199 | 200 | } 201 | 202 | #### 3. 修改中间件配置 203 | 204 | ~/yoyow-node-sdk/middleware/conf/config.js 205 | 206 | // api服务器地址 207 | apiServer: "ws://47.52.155.181:10011", 208 | 209 | // 安全请求有效时间,单位s 210 | secure_ageing: 60, 211 | 212 | // 平台安全请求验证key 由平台自定义 213 | secure_key: "", 214 | 215 | // 平台所有者资金私钥 (获取方式参考1. 创建测试网账号) 216 | active_key: "", 217 | 218 | // 平台所有者零钱私钥(获取方式参考1. 创建测试网账号) 219 | secondary_key: "", 220 | 221 | // 平台所有者备注私钥(获取方式参考1. 创建测试网账号) 222 | memo_key: "", 223 | 224 | // 平台id(yoyow id) 225 | platform_id: "", 226 | 227 | // 转账是否使用积分 228 | use_csaf: true, 229 | 230 | // 转账是否转到余额 否则转到零钱 231 | to_balance: true, 232 | 233 | // 钱包授权页URL 234 | wallet_url: "http://demo.yoyow.org:8000/#/authorize-service", 235 | 236 | // 允许接入的IP列表 237 | allow_ip: ["localhost", "127.0.0.1"] 238 | 239 | #### 4. 安装中间件服务所需node库 240 | 241 | 进入 ~/yoyow-node-sdk/middleware/ 目录 242 | 243 | npm install 244 | 245 | #### 5. 启动中间件服务 246 | 247 | npm start 248 |     249 | 启动正常情况如下图 250 | 251 | ![启动正常情况如图](https://github.com/bulangnisi/yoyow-node-sdk/blob/master/middleware/public/images/step4.png) 252 | 253 | ### 请求返回 error code 状态说明 254 | 255 | 1001 无效的签名类型 256 | 257 | 1002 无效的签名时间 258 | 259 | 1003 请求已过期 260 | 261 | 1004 无效的操作时间 262 | 263 | 1005 无效的操作签名 264 | 265 | 1006 账号信息与链上不匹配(常见于私钥恢复之后,使用其他电脑的本地数据或旧的备份文件进行授权操作导致) 266 | 267 | 1007 未授权该平台 268 | 269 | 2000 api底层异常 270 | 271 | 2001 账号不存在 272 | 273 | 2002 无效的账号 274 | 275 | 2003 无效的转账金额 276 | 277 | 2004 零钱和积分不足支付操作手续费 278 | 279 | 2005 零钱不足 280 | 281 | 2006 无效的资产符号或id 282 | 283 | 3001 文章ID必须为该平台该发文人的上一篇文章ID +1(平台管理发文id) 284 | 285 | ### 请求文档及示例 286 | 287 | #### 1. Api 相关 288 | 289 | ##### 1.1. 获取指定账户信息 getAccount 290 | 291 |  请求类型:GET 292 | 293 |  请求参数: 294 | 295 | {Number} uid - 账号id 296 | 297 |  请求示例: 298 | 299 | localhost:3000/api/v1/getAccount?uid=25638 300 | 301 |  返回结果: 302 | 303 | { 304 | code: 作结果, 305 | message: 返回消息, 306 | data: { // 用户信息 307 | uid: 账号uid 308 | name: 账号名称 309 | owner: 主控权限 310 | active: 资金权限 311 | secondary: 零钱权限 312 | memo_key: 备注密钥公钥 313 | reg_info: 注册信息 314 | can_post: 是否可发帖 315 | can_reply: 是否可回帖 316 | can_rate: 是否可评价 317 | is_full_member: 是否会员 318 | is_registrar: 是否注册商 319 | is_admin: 是否管理员 320 | statistics: { //用户YOYO资产详情 321 | obj_id: 资产对象id 322 | core_balance: 余额 323 | prepaid: 零钱 324 | csaf: 积分 325 | total_witness_pledge: 见证人总抵押(用户创建见证人抵押数量) 326 | total_committee_member_pledge: 理事会总抵押(用户创建理事会成员抵押数量) 327 | total_platform_pledge: 平台总抵押(用户创建平台抵押数量) 328 | releasing_witness_pledge: 见证人抵押待退回 329 | releasing_committee_member_pledge: 理事会抵押待退回 330 | releasing_platform_pledge: 平台抵押待退回 331 | } 332 | assets: [ //用户拥有的所有资产 333 | { 334 | amount: 资产数量, 335 | asset_id: 资产id, 336 | precision: 资产精度, 337 | symbol: 资产符号, 338 | description: 资产描述" 339 | } 340 | ... 341 | ] 342 | } 343 | } 344 | 345 | ##### 1.2. 获取指定账户近期活动记录 getHistory 346 | 347 |  请求类型:GET 348 | 349 |  请求参数: 350 | 351 | {Number} uid - 账号id 352 | 353 | {Number} op_type - 查询op类型 '0' 为 转账op,默认为null 即查询所有OP类型 354 | 355 | {Number} start 查询开始编号,为0时则从最新记录开始查询,默认为0 356 | 357 | {Number} limit - 查询长度,最大不可超过100条,默认为10 358 | 359 |  请求示例: 360 | 361 | localhost:3000/api/v1/getHistory?uid=25638&start=1220&limit=30&op_type=0 362 | 363 |  返回结果: 364 | 365 | { 366 | code: 操作结果, 367 | message: 返回消息, 368 | data: [] 历史记录对象数组 369 | } 370 | 371 | ##### 1.3. 转账到指定用户 transfer (需要安全验证的请求) 372 | 373 |  请求类型:POST 374 | 375 |  请求参数: 376 | 377 | {Object} cipher - 请求对象密文对象 378 | 379 | { 380 | 381 | ct, - 密文文本 16进制 382 | 383 | iv, - 向量 16进制 384 | 385 | s - salt 16进制 386 | 387 | } 388 | 389 | 请求对象结构: 390 | 391 | {Number} uid - 指定用户id 392 | 393 | {Number} amount - 转出金额 394 | 395 | {Number} asset_id - 资产id 396 | 397 | {string} memo - 备注 398 | 399 | {Number} time - 操作时间 400 | 401 | 请求示例:参照 安全请求验证 402 | 403 |  返回结果: 404 | 405 | { 406 | code: 操作结果, 407 | message: 返回消息, 408 | data: { 409 | block_num: 操作所属块号 410 | txid: 操作id 411 | } 412 | } 413 | 414 | ##### 1.4. 验证块是否不可退回 confirmBlock 415 | 416 |  请求类型:GET 417 | 418 |  请求参数: 419 | 420 | {Number} block_num - 验证的块号 421 | 422 |  请求示例: 423 | 424 | localhost:3000/api/v1/confirmBlock?block_num=4303231 425 | 426 |  返回结果: 427 | 428 | { 429 | code: 操作结果, 430 | message: 返回消息, 431 | data: 此块是否不可退回 432 | } 433 | 434 | ##### 1.5. 发送文章 post(需要安全验证的请求) 435 | 436 | 请求类型:POST 437 | 438 | 请求参数: 439 | 440 | 441 | {Object} cipher - 请求对象密文对象 442 | 443 | { 444 | 445 | ct, - 密文文本 16进制 446 | 447 | iv, - 向量 16进制 448 | 449 | s - salt 16进制 450 | 451 | } 452 | 453 | 请求对象结构: 454 | 455 | {Number} platform - 平台账号 456 | 457 | {Number} poster - 发文人账号 458 | 459 | {Number} post_pid - 文章编号 460 | 461 | {String} title - 文章标题 462 | 463 | {String} body - 文章内容 464 | 465 | {String} extra_data - 文章拓展信息 466 | 467 | {String} origin_platform - 原文平台账号(默认 null) 468 | 469 | {String} origin_poster - 原文发文者账号(默认 null) 470 | 471 | {String} origin_post_pid - 原文文章编号(默认 null) 472 | 473 | {Number} time - 操作时间 474 | 475 | 请求示例:参照 安全请求验证 476 | 477 |  返回结果: 478 | 479 | { 480 | code: 操作结果, 481 | message: 返回消息, 482 | data: { 483 | block_num: 操作所属块号 484 | txid: 操作id 485 | } 486 | } 487 | 488 | ##### 1.6. 更新文章 postUpdate(需要安全验证的请求) 489 | 490 | 请求类型:POST 491 | 492 | 请求参数: 493 | 494 | 495 | {Object} cipher - 请求对象密文对象 496 | 497 | { 498 | 499 | ct, - 密文文本 16进制 500 | 501 | iv, - 向量 16进制 502 | 503 | s - salt 16进制 504 | 505 | } 506 | 507 | 请求对象结构: 508 | 509 | {Number} platform - 平台账号 510 | 511 | {Number} poster - 发文人账号 512 | 513 | {Number} post_pid - 文章编号 514 | 515 | {String} title - 文章标题 516 | 517 | {String} body - 文章内容 518 | 519 | {String} extra_data - 文章拓展信息 520 | 521 | {Number} time - 操作时间 522 | 523 | 备注:修改文章操作时,title,body 和 extra_data 必须出现至少一个,并且与原文相同字段的内容不同 524 | 525 | 请求示例:参照 安全请求验证 526 | 527 |  返回结果: 528 | 529 | { 530 | code: 操作结果, 531 | message: 返回消息, 532 | data: { 533 | block_num: 操作所属块号 534 | txid: 操作id 535 | } 536 | } 537 | 538 | ##### 1.7. 获取文章 getPost 539 | 540 | 请求类型:GET 541 | 542 | 请求参数: 543 | 544 | {Number} platform - 平台账号 545 | 546 | {Number} poster -发文者账号 547 | 548 | {Number} post_pid - 文章编号 549 | 550 | 请求示例: 551 | 552 | http://localhost:3001/api/v1/getPost?platform=217895094&poster=210425155&post_pid=3 553 | 554 | 返回结果: 555 | 556 | { 557 | code: 操作结果, 558 | message: 返回消息, 559 | data: { 560 | "id":"1.7.12", - 文章ObjectId 561 | "platform":217895094, - 平台账号 562 | "poster":210425155, - 发文者账号 563 | "post_pid":5, - 文章编号 564 | "hash_value":"bb76a28981710f513479fa0d11fee154795943146f364da699836fb1f375875f", - 文章body hash值 565 | "extra_data":"{}", - 拓展信息 566 | "title":"test title in js for update", - 文章title 567 | "body":"test boyd in js for update", - 文章内容 568 | "create_time":"2018-03-12T10:22:03", - 文章创建时间 569 | "last_update_time":"2018-03-12T10:23:24", - 文章最后更新时间 570 | "origin_platform", - 原文平台账号 (仅对于创建文章时为转发时存在) 571 | "origin_poster", - 原文发文者账号 (仅对于创建文章时为转发时存在) 572 | "origin_post_pid" - 原文发文编号 (仅对于创建文章时为转发时存在) 573 | } 574 | } 575 | 576 | ##### 1.8. 获取文章列表 getPostList 577 | 578 | 请求类型:GET 579 | 580 | 请求参数: 581 | 582 | {Number} platform - 平台账号 583 | 584 | {Number} poster -发文者账号(默认null,为null时查询该平台所有文章) 585 | 586 | {Number} limit - 加载数(默认20) 587 | 588 | {String} start - 开始时间 'yyyy-MM-ddThh:mm:ss' ISOString (加载下一页时将当前加载出的数据的最后一条的create_time传入,不传则为从头加载) 589 | 590 | 请求示例: 591 | 592 | http://localhost:3001/api/v1/getPostList?platform=217895094&poster=210425155&limit=2&start=2018-03-12T09:35:36 593 | 594 | 返回结果: 595 | 596 | { 597 | code: 操作结果, 598 | message: 返回消息, 599 | data: [文章对象(参考获取单个文章返回的数据结构)] 600 | } 601 | ##### 1.9. 获取转账二维码文本(YOYOW APP 扫码可扫此二维码) 602 | 603 | 请求类型:GET 604 | 605 | 请求参数: 606 | 607 | {Number} amount - 收款金额 (与收款备注都不填写的情况,用户可在APP中输入) 608 | 609 | {String} memo - 收款备注 (与收款金额都不填写的情况,用户可在APP中输入) 610 | 611 | {String | Number} asset - 转账资产符号 或 资产ID(默认为YOYO资产) 612 | 613 | 请求示例: 614 | 615 | http://localhost:3001/api/v1/getQRReceive?amount=98&memo=新的转账&asset_id=0 616 | 617 |  返回结果: 618 | 619 | { 620 | code: 操作结果, 621 | message: 返回消息, 622 | data: 收款二维码字符串 623 | } 624 | 625 | ##### 1.10. 修改(仅增加白名单)授权用户资产白名单 updateAllowedAssets(需要安全验证的请求) 626 | 627 | 请求类型:POST 628 | 629 | 请求参数: 630 | 631 | 632 | {Object} cipher - 请求对象密文对象 633 | 634 | { 635 | 636 | ct, - 密文文本 16进制 637 | 638 | iv, - 向量 16进制 639 | 640 | s - salt 16进制 641 | 642 | } 643 | 644 | 请求对象结构: 645 | 646 | {Number} uid - 目标账户id 647 | 648 | {Number} asset_id - 资产id 649 | 650 | 请求示例:参照 安全请求验证 651 | 652 |  返回结果: 653 | 654 | { 655 | code: 操作结果, 656 | message: 返回消息, 657 | data: { 658 | block_num: 操作所属块号 659 | txid: 操作id 660 | } 661 | } 662 | 663 | ##### 1.11. 获取指定资产信息 getAsset 664 | 665 | 请求类型:GET 666 | 667 | 请求参数: 668 | 669 | {String | Number} search - 资产符号(大写)或 资产id 670 | 671 | 请求示例: 672 | 673 | http://localhost:3001/api/v1/getAsset?search=YOYOW 674 | 675 | 返回结果: 676 | 677 | { 678 | code: 操作结果, 679 | message: 返回消息, 680 | data: { 681 | "id":"1.3.0", - 资产object id 682 | "asset_id":0, - 资产id 683 | "symbol":"YOYO", - 资产符号 684 | "precision":5, - 资产精度 685 | "issuer":1264, - 资产发行者uid 686 | "options":{ 687 | "max_supply":"200000000000000", - 流通量上限 688 | "market_fee_percent":0, - 交易手续费百分比 689 | "max_market_fee":"1000000000000000", - 交易手续费最大值 690 | "issuer_permissions":0, - 资产可用权限 691 | "flags":0, - 资产权限 692 | "whitelist_authorities":[], - 资产白名单管理员清单 693 | "blacklist_authorities":[], - 资产黑名单管理员清单 694 | "whitelist_markets":[], - 交易对白名单 695 | "blacklist_markets":[], - 交易对黑名单 696 | "description":"" - 资产描述 697 | }, 698 | "dynamic_asset_data_id":"2.2.0", - 资产动态object id 699 | "dynamic_asset_data":{ 700 | "id":"2.2.0", - 资产动态object id 701 | "asset_id":0, 702 | "current_supply":"107384564466939", - 资产当前发行量 703 | "accumulated_fees":0 704 | }, 705 | "current_supply":"107384564466939", - 资产当前发行量 706 | "accumulated_fees":0 707 | } 708 | } 709 | 710 | ##### 1.12. 获取指定平台信息 getPlatformById 711 | 712 | 请求类型:GET 713 | 714 | 请求参数: 715 | 716 | {Number} uid - 平台所有者账号uid 717 | 718 | 请求示例: 719 | 720 | http://localhost:3001/api/v1/getPlatformById?uid=217895094 721 | 722 | 返回结果: 723 | 724 | { 725 | 726 | "id": "1.6.0", - 平台 object id 727 | "owner": 217895094, - 平台所有者账号uid 728 | "name": "test-yoyow", - 平台名称 729 | "sequence": 1, 730 | "is_valid": true, - 是否有效 731 | "total_votes": 0, - 平台总票数 732 | "url": "http://demo.yoyow.org/", - 平台url地址 733 | "pledge": 1000000000, - 平台抵押(YOYO) 734 | "pledge_last_update": "2018-02-10T01:03:57", - 平台抵押最后更新时间 735 | "average_pledge": 176601774, - 平台平均抵押 736 | "average_pledge_last_update": "2018-02-11T06:49:12", - 平台平均抵押最后更新时间 737 | "average_pledge_next_update_block": 4562164, - 平台平均抵押下次更新块号 738 | "extra_data": "{}", - 平台拓展信息 739 | "create_time": "2018-02-10T01:03:57", - 平台创建日期 740 | "last_update_time": "2018-02-11T06:49:12" - 平台最后更新日期 741 | 742 | } 743 | 744 | #### 2. Auth 相关 745 | 746 | ##### 2.1. 签名平台 sign 747 | 748 | 请求类型:GET 749 | 750 | 请求参数:无 751 | 752 | 请求示例: 753 | 754 | localhost:3000/auth/sign 755 | 756 | 返回结果: 757 | 758 | { 759 | code: 操作结果, 760 | message: 返回消息, 761 | data: { 762 | sign: 签名结果, 763 | time: 操作时间毫秒值, 764 | platform: 签名平台所有人id, 765 | url: 钱包授权url 766 | } 767 | } 768 | 769 | ##### 2.2 签名验证 verify 770 | 771 | 请求类型:GET 772 | 773 | 请求参数: 774 | 775 | {Number} yoyow - 账号id 776 | 777 | {Number} time - 操作时间毫秒值 778 | 779 | {String} sign - 签名结果 780 | 781 | 请求示例: 782 | 783 | localhost:3000/auth/verify?sign=20724e65c0d763a0cc99436ab79b95c02fbb3f352e3f9f749716b6dac84c1dc27e5e34ff8f0499ba7d94f1d14098c6a60f21f2a24a1597791d8f7dda47559c39a0&time=1517534429858&yoyow=217895094 784 | 785 | 返回结果: 786 | 787 | { 788 | code: 操作结果, 789 | message: 返回消息, 790 | data: { 791 | verify: 签名是否成功, 792 | name: 签名的yoyow用户名 793 | } 794 | } 795 | 796 | ##### 2.3 签名平台 返回二维码 signQR 797 | 798 | 请求类型:GET 799 | 800 | 请求参数: 801 | 802 | {String} state - 拓展信息,将在调用平台登录接口时与用户签名信息一同发送到平台,用于平台登陆接口需要自定义的参数时使用,若无此需求可不传 803 | 804 | 请求示例: 805 | 806 | localhost:3000/auth/signQR?state=platformCustomParams 807 | 808 | 返回结果: 809 | 810 | { 811 | code: 操作结果, 812 | message: 返回消息, 813 | data: 二维码图片base64 字符串 814 | } 815 | 816 | ### 安全请求验证 817 | 818 | 涉及到资金安全相关的操作会在中间件服务中验证其有效性 819 | 820 | 使用方自定义key配置于 config 中的 secure_key 里 821 | 822 | 将操作对象加密传入 823 | 824 | 加密示例(javascript的 crypto-js 版,其他语言使用类似的AES加密方式) 825 | 826 | 默认 mode CBC , padding scheme Pkcs7 827 | 828 | transfer操作 829 | 830 | let key = 'customkey123456'; // 此key与中间件中的config 里 secure_key相同 831 | 832 | let sendObj = { 833 | "uid": 9638251, 834 | "amount": 100, 835 | "memo": "hello yoyow", 836 | "time": Date.now() 837 | } 838 | 839 | time 字段 操作时间取当前时间毫秒值 加密操作须带有此字段 用于验证操作时效 840 | 841 | let cipher = CryptoJS.AES.encrypt(JSON.stringify(sendObj), key); 842 | 843 | $.ajax({ 844 | url: 'localhost:3000/api/v1/transfer', 845 | type: 'POST', 846 | data: { 847 | ct: cipher.ciphertext.toString(CryptoJS.enc.Hex), 848 | iv: cipher.iv.toString(), 849 | s: cipher.salt.toString() 850 | }, 851 | success: function(res){ 852 | // do something ... 853 | } 854 | }) 855 | 856 | PHP加密方式 857 | 858 | function cryptoJsAesEncrypt($passphrase, $value){ 859 | $salt = openssl_random_pseudo_bytes(8); 860 | $salted = ''; 861 | $dx = ''; 862 | while (strlen($salted) < 48) { 863 | $dx = md5($dx.$passphrase.$salt, true); 864 | $salted .= $dx; 865 | } 866 | $key = substr($salted, 0, 32); 867 | $iv = substr($salted, 32,16); 868 | $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, true, $iv); 869 | $data = array("ct" => bin2hex($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt)); 870 | return json_encode($data); 871 | } 872 | 873 | 如 请求文档及示例 1.3. 转账到指定用户 transfer 874 | 875 | 其他需要安全请求验证的操作根据文档改动sendObj -------------------------------------------------------------------------------- /middleware/README-EN.md: -------------------------------------------------------------------------------- 1 | # yoyow-middleware 2 | 3 | ### Start 4 | 5 | #### 1. Create test-net account 6 | 7 | Address of test-net is [http://demo.yoyow.org:8000](http://demo.yoyow.org:8000 "test-net address of yoyow wallet"). 8 | 9 | The CLI program can be obtained from [https://github.com/yoyow-org/yoyow-core-testnet/releases/](https://github.com/yoyow-org/yoyow-core-testnet/releases/). 10 | 11 | ![create account](https://github.com/chen188/yoyow-node-sdk/blob/master/middleware/public/images/step1-en.png) 12 | 13 | Steps to find all your public/private keys: 14 | 15 | - log into [wallet](http://demo.yoyow.org:8000 "test-net address of yoyow wallet"). 16 | - goto `Settings` on the left side 17 | - click the `Account` and all public keys come to you, including `Owner`, `Active`,`Secondary` and `Memo` key 18 | - if you want to check the private key, click `Show Private Key` besides public key and type in you `wallet password` accordingly. 19 | 20 | ![find all keys](https://github.com/chen188/yoyow-node-sdk/blob/master/middleware/public/images/step3-en.png) 21 | 22 | #### 2. Create platform 23 | 24 | To create a platform, 11000 YOYOs(TESTs in test-net) are needed at least, 10000 YOYOs for deposition and 1000 YOYOs for the registration fee. 25 | (12000 TESTs are transferred to you when your registration is finished, which is enough to create a platform) 26 | 27 | ##### 2.1 Run cli-wallet 28 | ###### 2.1.1 run with parameters 29 | 30 | # for Ubuntu 31 | ./yoyow_client -s ws://47.52.155.181:10011 --chain-id 3505e367fe6cde243f2a1c39bd8e58557e23271dd6cbf4b29a8dc8c44c9af8fe 32 | 33 | # used when the permission problem comes to you 34 | sudo chmod a+x * 35 | 36 | 37 | ###### 2.1.2 run with configuration file 38 | 39 | create file `wallet.json` under the same directory with `cli-wallet`, filling it with: 40 | 41 | { 42 | "chain_id": "3505e367fe6cde243f2a1c39bd8e58557e23271dd6cbf4b29a8dc8c44c9af8fe", 43 | "pending_account_registrations": [], 44 | "pending_witness_registrations": [], 45 | "labeled_keys": [], 46 | "blind_receipts": [], 47 | "ws_server": "ws://47.52.155.181:10011", 48 | "ws_user": "", 49 | "ws_password": "" 50 | } 51 | 52 | and then choose the corresponding command of your OS, 53 | 54 | # for Ubuntu 55 | ./yoyow_client 56 | 57 | ##### 2.2 set wallet password 58 | 59 | Connect to the server successfully and you will see: 60 | 61 | Please use the set_password method to initialize a new wallet before continuing 62 | new >>> 63 | 64 | Setup your password by: 65 | 66 | new >>> set_password 67 | 68 | 69 | set_password 70 | null 71 | locked >>> 72 | 73 | and then unlock your wallet: 74 | 75 | locked >>> unlock 76 | 77 | 78 | unlock 79 | null 80 | unlocked >>> 81 | 82 | 83 | ##### 2.3 Import active private key 84 | 85 | unlocked >>> import_key 86 | 87 | for example: 88 | 89 | unlocked >>> import_key 120252179 5JwREzpwb62iEcD6J6WXs2fbn1aSKWQWvGLNCqAEYwS31EHD7i4 90 | 91 | will give you: 92 | 93 | 1937037ms th_a wallet.cpp:820 save_wallet_file ] saving wallet to file wallet.json 94 | true 95 | 96 | if not `true` returned, please make sure your uid and private key are correct. 97 | 98 | ##### 2.4 Create platform 99 | 100 | unlocked >>> create_platform <"platform name"> "" "" true 101 | 102 | for example: 103 | 104 | unlocked >>> create_platform 235145448 "myPlatform" 10000 YOYO "www.example.com" "{}" true 105 | 106 | will gives you: 107 | 108 | { 109 | "ref_block_num": 33094, 110 | "ref_block_prefix": 2124691028, 111 | "expiration": "2018-02-07T08:40:30", 112 | "operations": [[ 113 | 20,{ 114 | "fee": { 115 | "total": { 116 | "amount": 1029296, 117 | "asset_id": 0 118 | }, 119 | "options": { 120 | "from_csaf": { 121 | "amount": 1029296, 122 | "asset_id": 0 123 | } 124 | } 125 | }, 126 | "account": 235145448, 127 | "pledge": { 128 | "amount": 1000000000, 129 | "asset_id": 0 130 | }, 131 | "name": "myPlatform", 132 | "url": "www.example.com", 133 | "extra_data": "{}" 134 | } 135 | ] 136 | ], 137 | "signatures": [ 138 | "1f08b704dd5ccf7e05e5dec45b06ad41e6382f5dd528e3f644d52ff4fb29c2040507544d5e94b84d77d70edcd68bb35b0cded0db87816ae64979ba98eeb641d5d7" 139 | ] 140 | } 141 | 142 | ##### 2.5 Update platform 143 | 144 | unlocked >>> update_platform <"platform name"> "" "" true 145 | 146 | For example: 147 | 148 | unlocked >>> update_platform 235145448 "newplatformname" 10000 YOYO null null true 149 | 150 | The result returned is same as `Create platform`. 151 | 152 | > Note: leave `platform name`, `url for platform` or `extra information` to `null` will not change the corresponding value, 153 | that's to say, as in the previous example,the `url for platform` and `extra information` won't be changed. 154 | 155 | ##### 2.6 Login by scanning QR code 平台扫码登录协议 156 | 157 | The `login` field in platform property `extra_data` is used by platform login through scanning QR code, e.g. ```"extra_data": "{\"login\":\"http://localhost:8280/login\"}``` 158 | 159 | Authorization by scanning QR code in APP will access this address and send back the user signature object. 160 | 161 | { 162 | {Number} yoyow - the yoyow id of current user 163 | {String} time - signature timestramp in ms 164 | {String} sign - signation 165 | {String} state - platform defined information when requesting(See chapter 2.3 - signQR) 166 | } 167 | 168 | Following information is expected from platform specified API 169 | 170 | { 171 | {Number} code - operation result. 0 indicates passing, any non-zero condition should be treated as an error. 172 | {String} message - operation comment. 173 | } 174 | 175 | #### 3. Customize configuration of middleware 176 | 177 | ~/yoyow-node-sdk/middleware/conf/config.js 178 | 179 | // api server 180 | apiServer: "ws://47.52.155.181:10011", 181 | 182 | // seconds for a valid secure request 183 | secure_ageing: 60, 184 | 185 | // platform specified secure key used for safe request 186 | secure_key: "", 187 | 188 | // Platform's Secondary key(can be obtained as chapter 1. Create test-net account) 189 | secondary_key: "", 190 | 191 | // Platform's MEMO key(can be obtained as chapter 1. Create test-net account) 192 | memo_key: "", 193 | 194 | // Platform's uid(yoyow id) 195 | platform_id: "", 196 | 197 | // whether to use csaf for transfer operation fee or not 198 | use_csaf: true, 199 | 200 | // transfer to balance? otherwise to tipping 201 | to_balance: true, 202 | 203 | // authorizing url of wallet 204 | wallet_url: "http://demo.yoyow.org:8000/#/authorize-service", 205 | 206 | // IPs allow to access 207 | allow_ip: ["localhost", "127.0.0.1"] 208 | 209 | #### 4. Install dependencies of middleware 210 | 211 | cd ~/yoyow-node-sdk/middleware/ 212 | npm install 213 | 214 | #### 5. Start middleware service 215 | 216 | npm start 217 |     218 | When startup normally and successfully, you'll see: 219 | 220 | ![startup normally and successfully](https://github.com/chen188/yoyow-node-sdk/blob/master/middleware/public/images/step4.png) 221 | 222 | ### Details on response code(error code) 223 | 224 | 1001 Invalid signature 225 | 1002 Invalid time of signature 226 | 1003 Expired request 227 | 1004 Invalid operation time 228 | 1005 Invalid signature of the request 229 | 1006 Inconsistent with On-Chain account information(Mostly caused by using other computers' local data or old backup files for authorization operations after private key recovery) 230 | 1007 Unauthorized platform 231 | 2000 Internal API error 232 | 2001 Account not exists 233 | 2002 Invalid account 234 | 2003 Invalid transfer amount 235 | 2004 Insufficient Tipping and Bonus Point to afford fees 236 | 2005 Insufficient Tipping 237 | 3001 Article ID must equal with this platform's latest article id + 1. (these IDs are managed by platform) 238 | 239 | ### Documents on API 240 | 241 | #### 1. API relevant 242 | 243 | ##### 1.1. getAccount - get yoyow account information specified by `uid` 244 | 245 |  type: GET 246 | 247 |  params: 248 | 249 | {Number} uid - yoyow id 250 | 251 | example: 252 | 253 | localhost:3000/api/v1/getAccount?uid=25638 254 | 255 | # response: 256 | { 257 | code: res_code, 258 | message: res_message, 259 | data: { // account info. 260 | uid: yoyow id 261 | name: account name 262 | owner: owner permission 263 | active: active permission 264 | secondary: secondary permission 265 | memo_key: public key of memo key 266 | reg_info: registration information 267 | can_post: able to post articels 268 | can_reply: able to reply to articels 269 | can_rate: able to rate articels 270 | is_full_member: is full member 271 | is_registrar: is registrar 272 | is_admin: is admin 273 | statistics: { //account's balances 274 | obj_id: balance object id 275 | core_balance: balance 276 | prepaid: tipping 277 | csaf: csaf 278 | total_witness_pledge: witess's total pledge(pledged when creating witness) 279 | total_committee_member_pledge: committee's total pledges(pledged when creating committee) 280 | total_platform_pledge: platform's total pledge(pledged when creating platform) 281 | releasing_witness_pledge: releasing witness pledge 282 | releasing_committee_member_pledge: releasing committee member pledge 283 | releasing_platform_pledge: releasing platform pledge 284 | } 285 | } 286 | } 287 | 288 | ##### 1.2. getHistory - get recently activity records of account `uid` 289 | 290 |  type: GET 291 | 292 |  params: 293 | 294 | {Number} uid - yoyow id 295 | {Number} page - page number 296 | {Number} size - records number per page 297 | 298 |  example: 299 | 300 | localhost:3000/api/v1/getHistory?uid=25638&page=1&size=10 301 | 302 |  response: 303 | 304 | { 305 | code: res_code, 306 | message: res_message, 307 | data: { 308 | maxPage: max page number, 309 | curPage: current page number, 310 | total: total records, 311 | size: records per page, 312 | list: recently activity records 313 | } 314 | } 315 | 316 | ##### 1.3. transfer - transfer to uid (security verification needed) 317 | 318 |  type: POST 319 | 320 |  params: 321 | 322 | {Object} cipher - cipher object of request 323 | 324 | { 325 | ct, - Hexadecimal ciphertext 326 | iv, - Hexadecimal vector 327 | s - Hexadecimal salt 328 | } 329 | 330 | #structure of request: 331 | 332 | {Number} uid - transfer to `uid` 333 | {Number} amount - amount to transfer 334 | {string} memo - memo 335 | {Number} time - operation time 336 | 337 | example: see chapter `Verification of request` 338 | 339 |  response: 340 | 341 | { 342 | code: res_code, 343 | message: res_message, 344 | data: { 345 | block_num: which block this operation belongs to 346 | txid: transaction id of this operation 347 | } 348 | } 349 | 350 | ##### 1.4. confirmBlock - check the `block_number` block is irreversible(confirmed) 351 | 352 |  type: GET 353 | 354 |  params: 355 | 356 | {Number} block_num - block to check 357 | 358 |  example: 359 | 360 | localhost:3000/api/v1/confirmBlock?block_num=4303231 361 | 362 |  response: 363 | 364 | { 365 | code: res_code, 366 | message: res_message, 367 | data: whether block is irreversible 368 | } 369 | 370 | ##### 1.5. post - post an post(security verification needed) 371 | 372 | type: POST 373 | 374 | params: 375 | 376 | {Object} cipher - cipher object of request 377 | 378 | { 379 | ct, - Hexadecimal ciphertext 380 | iv, - Hexadecimal vector 381 | s - Hexadecimal salt 382 | } 383 | 384 | #structure of request: 385 | 386 | {Number} platform - yoyow id of platform 387 | {Number} poster - yoyow id of poster 388 | {Number} post_pid - id of this post 389 | {String} title - title of this post 390 | {String} body - content of this post 391 | {String} extra_data - extra info of this post 392 | {String} origin_platform - origin platform of post(defaults to null) 393 | {String} origin_poster - origin poster of post(defaults to null) 394 | {String} origin_post_pid - origin post id(defaults to null) 395 | {Number} time - operation time 396 | 397 | example: see chapter `Verification of request` 398 | 399 |  response: 400 | 401 | { 402 | code: res_code, 403 | message: res_message, 404 | data: { 405 | block_num: which block this operation belongs to 406 | txid: transaction id of this operation 407 | } 408 | } 409 | 410 | ##### 1.6. postUpdate - update Post (security verification needed) 411 | 412 | type: POST 413 | 414 | params: 415 | 416 | {Object} cipher - cipher object of request 417 | 418 | { 419 | ct, - Hexadecimal ciphertext 420 | iv, - Hexadecimal vector 421 | s - Hexadecimal salt 422 | } 423 | 424 | #structure of request: 425 | 426 | {Number} platform - yoyow id of platform 427 | {Number} poster - yoyow id of poster 428 | {Number} post_pid - id of this post 429 | {String} title - title of this post 430 | {String} body - content of this post 431 | {String} extra_data - extra info of this post 432 | {Number} time - operation time 433 | 434 | > Note: when updating a post, at least one field of title, body, and extra_data should be present and different with original 435 | 436 | example: see chapter `Verification of request` 437 | 438 |  response: 439 | 440 | { 441 | code: res_code, 442 | message: res_message, 443 | data: { 444 | block_num: which block this operation belongs to 445 | txid: transaction id of this operation 446 | } 447 | } 448 | 449 | ##### 1.7. getPost - get a specified post 450 | 451 | type: GET 452 | 453 | params: 454 | 455 | {Number} platform - yoyow id of platform 456 | {Number} poster - poster id 457 | {Number} post_pid - id of this post 458 | 459 | example: 460 | 461 | http://localhost:3001/api/v1/getPost?platform=217895094&poster=210425155&post_pid=3 462 | 463 | response: 464 | 465 | { 466 | code: res_code, 467 | message: res_message, 468 | data: { 469 | "id":"1.7.12", - ObjectId of this post 470 | "platform":217895094, - yoyow id of platform 471 | "poster":210425155, - yoyow id of poster 472 | "post_pid":5, - id of this post 473 | "hash_value":"bb76a28981710f513479fa0d11fee154795943146f364da699836fb1f375875f", - hash value of the content 474 | "extra_data":"{}", - extra information of post 475 | "title":"test title in js for update", - title of this post 476 | "body":"test boyd in js for update", - content of this post 477 | "create_time":"2018-03-12T10:22:03", - created at 478 | "last_update_time":"2018-03-12T10:23:24", - last updated at 479 | "origin_platform", - origin platform where the post firstly came to people(only used when reposting an existing post) 480 | "origin_poster", - poster id in orgin platform (only used when reposting an existing post) 481 | "origin_post_pid" - post id in origin platform (only used when reposting an existing post) 482 | } 483 | } 484 | 485 | ##### 1.8. getPostList - get a list of posts 486 | 487 | type: GET 488 | 489 | params: 490 | 491 | {Number} platform - yoyow id of platform 492 | {Number} poster - poster id(default to null and get all posts of this platform) 493 | {Number} limit - limit to(default to 20) 494 | {String} start - start from 'yyyy-MM-ddThh:mm:ss' ISOString 495 | (`create_time` of the last record should be used as `start` in next request to traverse all records) 496 | 497 | example: 498 | 499 | http://localhost:3001/api/v1/getPostList?platform=217895094&poster=210425155&limit=2&start=2018-03-12T09:35:36 500 | 501 | response: 502 | 503 | { 504 | code: res_code, 505 | message: res_message, 506 | data: [object of post(see `getPost`)] 507 | } 508 | ##### 1.9. transferFromUser - transfer between users through platform (security verification needed) 509 | 510 | type: POST 511 | 512 | params: 513 | 514 | {Object} cipher - cipher object of request 515 | 516 | { 517 | ct, - Hexadecimal ciphertext 518 | iv, - Hexadecimal vector 519 | s - Hexadecimal salt 520 | } 521 | 522 | #structure of request: 523 | 524 | {Number} from - transfer from yoyow id 525 | {Number} to - transfer to yoyow id 526 | {Number} amount - amount to transfer 527 | {String} memo - memo 528 | {Number} time - operation time 529 | 530 | example: see chapter `Verification of request` 531 | 532 |  response: 533 | 534 | { 535 | code: res_code, 536 | message: res_message, 537 | data: { 538 | block_num: which block this operation belongs to 539 | txid: transaction id of this operation 540 | } 541 | } 542 | 543 | #### 2. Auth relevant 544 | 545 | ##### 2.1. sign - sign platform 546 | 547 | type: GET 548 | 549 | params: NULL 550 | 551 | example: 552 | 553 | localhost:3000/auth/sign 554 | 555 | response: 556 | 557 | { 558 | code: res_code, 559 | message: res_message, 560 | data: { 561 | sign: the result of sign, 562 | time: operation time in ms, 563 | platform: yoyow id of the platform owner, 564 | url: authorizing URL of wallet 565 | } 566 | } 567 | 568 | ##### 2.2 verify - verify the signature 569 | 570 | type: GET 571 | 572 | params: 573 | 574 | {Number} yoyow - yoyow id 575 | {Number} time - operation time in ms 576 | {String} sign - the result of sign 577 | 578 | example: 579 | 580 | localhost:3000/auth/verify?sign=20724e65c0d763a0cc99436ab79b95c02fbb3f352e3f9f749716b6dac84c1dc27e5e34ff8f0499ba7d94f1d14098c6a60f21f2a24a1597791d8f7dda47559c39a0&time=1517534429858&yoyow=217895094 581 | 582 | response: 583 | 584 | { 585 | code: res_code, 586 | message: res_message, 587 | data: { 588 | verify: is signature vaild, 589 | name: yoyow name of this signature 590 | } 591 | } 592 | 593 | ##### 2.3 signQR - sign platform and returned as QR code 594 | 595 | type:GET 596 | 597 | params: 598 | 599 | {String} state - optional extension inforamation in addition to signature data sended to platform login interface, 600 | only used when platform defined paramenters are needed. 601 | 602 | example: 603 | 604 | localhost:3000/auth/signQR?state=platformCustomParams 605 | 606 | response: 607 | 608 | { 609 | code: res_code, 610 | message: res_message, 611 | data: base64 encoded QR code image 612 | } 613 | 614 | ### Verification of request 615 | 616 | Operations manipulating money will be validated in middleware service. 617 | 618 | `secure_key` in `config` is the place where the platform can customize the key. 619 | 620 | Encrypt the operation object for sending a request. 621 | 622 | Encryption example(JS version). 623 | 624 | > Default mode is CBC, padding scheme is Pkcs7. 625 | 626 | #transfer operation 627 | 628 | let key = 'customkey123456'; // the `secure_key` in `config` 629 | 630 | let sendObj = { 631 | "uid": 9638251, 632 | "amount": 100, 633 | "memo": "hello yoyow", 634 | "time": Date.now(), // the current time in ms, required 635 | } 636 | 637 | let cipher = CryptoJS.AES.encrypt(JSON.stringify(sendObj), key); 638 | 639 | $.ajax({ 640 | url: 'localhost:3000/api/v1/transfer', 641 | type: 'POST', 642 | data: { 643 | ct: cipher.ciphertext.toString(CryptoJS.enc.Hex), 644 | iv: cipher.iv.toString(), 645 | s: cipher.salt.toString() 646 | }, 647 | success: function(res){ 648 | // do something ... 649 | } 650 | }) 651 | 652 | Encryption example in PHP: 653 | 654 | function cryptoJsAesEncrypt($passphrase, $value){ 655 | $salt = openssl_random_pseudo_bytes(8); 656 | $salted = ''; 657 | $dx = ''; 658 | while (strlen($salted) < 48) { 659 | $dx = md5($dx.$passphrase.$salt, true); 660 | $salted .= $dx; 661 | } 662 | $key = substr($salted, 0, 32); 663 | $iv = substr($salted, 32,16); 664 | $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, true, $iv); 665 | $data = array("ct" => bin2hex($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt)); 666 | return json_encode($data); 667 | } 668 | 669 | 670 | > Any other operation needs security verification should replace the `sendObj` accordingly. --------------------------------------------------------------------------------