├── .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 | 
12 |
13 | 平台所有者的各权限私钥获取方式 登录钱包 》 左侧菜单设置 》 账号 》 查看权限 》 在对应权限密钥的右侧点击显示私钥 》 输入密码显示私钥 》 将看到的私钥拷贝进配置中.
14 |
15 | 
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 | 
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 | 
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 | 
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 | 
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.
--------------------------------------------------------------------------------