├── .gitignore
├── README.md
├── app.js
├── bin
└── www
├── blockchain_lib
├── conf.js
└── lib.js
├── bower.json
├── help_utils
├── base64_util.js
└── geth_level_utils.js
├── imgs
├── 创建资产.png
├── 发行资产.png
├── 发行资产详情.png
├── 注册.png
├── 登录.png
├── 账户1交易记录.png
├── 账户1信息.png
├── 账户1发起交易.png
├── 账户1发起交易详情.png
├── 账户1资产列表.png
├── 账户2交易记录.png
├── 账户2信息.png
├── 账户2接收交易详情.png
└── 账户2资产列表.png
├── models
├── Users.js
├── asset.js
└── transaction.js
├── ng_app
├── .bowerrc
├── css
│ ├── font-awesome.min.css
│ └── my.css
├── fonts
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── img
│ ├── aif.svg
│ ├── group-logo.svg
│ ├── group.svg
│ ├── logo.png
│ ├── logo2.png
│ ├── logo3.png
│ └── logo4.png
├── index.html
├── js
│ ├── controllers.js
│ ├── directives.js
│ ├── interceptors.js
│ ├── loginAndRegisterModule.js
│ ├── menu.js
│ ├── router.js
│ └── services.js
└── views
│ ├── assetCreate.html
│ ├── assetDetail.html
│ ├── assetIssue.html
│ ├── assetList.html
│ ├── changePwd.html
│ ├── dashboard.html
│ ├── login.html
│ ├── register.html
│ ├── test.html
│ ├── transactionDetail.html
│ ├── transactionList.html
│ ├── transfer.html
│ ├── transferConfirm.html
│ ├── user.html
│ └── verifyPwd.html
├── package-lock.json
├── package.json
├── public
└── stylesheets
│ └── style.css
├── routes
├── Users.js
├── asset.js
├── index.js
├── password.js
├── token.js
└── transaction.js
└── views
├── error.jade
├── index.jade
└── layout.jade
/.gitignore:
--------------------------------------------------------------------------------
1 | #os
2 | .DS_Store
3 |
4 | #ide
5 | .idea/
6 | *.iml
7 | *.sublime-workspace
8 |
9 | #python
10 | venv/
11 | __pycache__/
12 | *.pyc
13 |
14 | #front
15 | node_modules/
16 | bower_components/
17 |
18 | #others
19 | .vagrant/
20 | *.sqlite
21 | *.log
22 | *.bak
23 | nohup.out
24 | /npm-debug.log
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 浙大区块链实验室
2 | ---
3 | 本文将介绍区块链的使用,以帮助读者理解资产创建、发行、交易的整个流程。
4 | ## 准备
5 |
6 | 获取代码
7 | `git clone https://github.com/hyperchaincn/Digital-Wallet.git`
8 |
9 | 配置开发者平台账号信息
10 | * [注册](https://dev.hyperchain.cn)趣链科技开发者平台账号
11 | * 登陆后创建应用
12 | * 进入应用详情,获取AppKey、AppSecret
13 | * 编辑项目目录下`blockchain_lib/conf.js`文件,将AppKey、AppSecret以及账号和密码替换至相应位置
14 |
15 | 配置数据库
16 | * 下载并安装mongodb
17 | * [mongodb安装地址](https://www.mongodb.com/download-center#community)
18 |
19 | 部署项目
20 | * 安装nodejs
21 | * [nodejs安装地址](https://nodejs.org/zh-cn/)
22 | * 安装bower
23 | * [bower安装地址](https://bower.io/)
24 | * 安装依赖并启动 `npm start`
25 | * 端口访问 `localhost:3000`
26 |
27 |
28 |
29 | ---
30 | ## 简介
31 | ```
32 | 这里我们采用固定合约源码并给出了编译合约生成的的abi(合约源码对应的abi数组)以及bin(合约编译而成的字节码),这里用户也可以自己给定合约源码以及编译生成的bin,abi。
33 | const TOKEN_SOURCE = 'contract Token { address issuer; mapping (address => uint) balances; event Issue(address account, uint amount); event Transfer(address from, address to, uint amount); function Token() { issuer = msg.sender; } function issue(address account, uint amount) { if (msg.sender != issuer) throw; balances[account] += amount; } function transfer(address to, uint amount) { if (balances[msg.sender] < amount) throw; balances[msg.sender] -= amount; balances[to] += amount; Transfer(msg.sender, to, amount); } function getBalance(address account) constant returns (uint) { return balances[account]; } }';
34 | const TOKEN_BIN = '0x6060604052341561000c57fe5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101ca806100396000396000f300606060405263ffffffff60e060020a600035041663867904b48114610037578063a9059cbb14610058578063f8b2cb4f14610079575bfe5b341561003f57fe5b610056600160a060020a03600435166024356100a7565b005b341561006057fe5b610056600160a060020a03600435166024356100e6565b005b341561008157fe5b610095600160a060020a036004351661017f565b60408051918252519081900360200190f35b60005433600160a060020a039081169116146100c35760006000fd5b600160a060020a03821660009081526001602052604090208054820190555b5050565b600160a060020a0333166000908152600160205260409020548190101561010d5760006000fd5b600160a060020a0333811660008181526001602090815260408083208054879003905593861680835291849020805486019055835192835282015280820183905290517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360600190a15b5050565b600160a060020a0381166000908152600160205260409020545b9190505600a165627a7a72305820299e9bb6a492d60cb690d97c76ac26d821ff6bba1b863ce1b8720e449789692c0029'
35 | const TOKEN_ABI = '[{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"issue","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Issue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event"}]'
36 |
37 |
38 | ```
39 | ## 注册
40 |
41 | ```
42 | 打开实验室链接地址,并利用手机号码注册一个交易帐号,同时会生成账号的地址address。
43 |
44 | ```
45 | ```
46 | 这里要调用远程api createaccount接口生成账户地址信息
47 | request({
48 | url: "https://api.hyperchain" + "/v1/dev/account/create" ,
49 | method: "GET",
50 | json: true,
51 | headers: {
52 | "Accept": "Accept: text/html",
53 | "Authorization": accessToken
54 | }
55 | },
56 | ```
57 |
58 | [实验室链接地址](http://localhost:3000)
59 |
60 | 
61 | ---
62 |
63 | ## 登录
64 | ```
65 | 登录已注册的账号
66 | ```
67 | 
68 |
69 | ---
70 | ## 账户信息
71 | ```
72 | 账户信息,包括账户名称,交易地址,通过地址能够进行后续的创建货币、发行货币以及进行交易
73 | ```
74 | 
75 |
76 | ---
77 |
78 | ## 创建资产
79 | ```
80 | 这里创建资产需要填写资产的名称,资产的单位以及资产的描述信息,填写之后点击创建按钮,
81 | 就能够创建一份我们自定义的财产,同时页面跳转到资产的详细信息。资产列表新加一项内容,
82 | 后台将合约源码部署到了区块链上,之后我们能够进行发行以及交易等操作。
83 | ```
84 | ```
85 | 调用远程api deploy接口将合约部署到区块链上
86 | request({
87 | url: "https://api.hyperchain" + "/v1/dev/contract/deploy",
88 | method: "POST",
89 | json: true,
90 | headers: {
91 | "content-type": "application/json",
92 | "Accept": "application/json",
93 | "Authorization": accessToken
94 | }
95 | , body: requestData
96 | }
97 | ```
98 | ```
99 | 资产详情中我们能够看到资产的名称等信息,主要信息为资产的地址即为合约部署到区块链上的地址,
100 | 发行方账户地址即为我们发行账户的地址。但是发行总数量以及资产余额为零,这些值的改变需要后
101 | 续发行资产等操作。
102 | ```
103 | * 填写资产信息
104 | 
105 | ---
106 | ## 发行资产
107 |
108 | ```
109 | 发行资产为根据方法方账户以及合约的地址发行的资产,该资产为发行方自定义的资产,可以指定资产的总数,
110 | 这里我们发行100000个货币。资产详情中有我们发行的资产的详细信息,资产地址为我们部署到区块链上的地址。
111 | ```
112 | ```
113 | 发行资产对应着合约中abi的一下部分,主要使用了源码中发行(issue)方法发行货币,可以通过abi等信息生成payload用于invokecontract(调用合约)使用
114 | var theAbi = {
115 | "constant": false,
116 | "inputs": [{"name": "account", "type": "address"}, {"name": "amount", "type": "uint256"}],
117 | "name": "issue",
118 | "outputs": [],
119 | "payable": false,
120 | "type": "function"
121 | };
122 | var methodParams = [from, amount];
123 | ...
124 | var encodedparams = encodeMethodParams(theAbi, methodParams);
125 | request({
126 | url: "https://api.hyperchain" + "/v1/dev/contract/invoke",
127 | method: "POST",
128 | json: true,
129 | headers: {
130 | "content-type": "application/json",
131 | "Accept": "application/json",
132 | "Authorization": accessToken
133 | }
134 | , body: requestData
135 | }
136 |
137 | ```
138 |
139 | * 发行资产
140 | *
141 | 
142 | * 资产详情
143 | 
144 | ---
145 | ## 交易
146 | ```
147 | 这里注册另一个账户后可以实现账户之间的交易,首先我们注册另一个账户,账户1,输入交易金融5000以及
148 | 对方账户地址之后发起交易。账户2,得到交易金额5000以及交易详细信息。此时账户1的金额会减少5000在
149 | 交易详细信息中能够看到,账户2的交易信息增加5000,交易详情中能够看到
150 | ```
151 | ```
152 | 账户1交易详情中能够看到资产名称为我们定义好的比特币,交易hash为这次交易生成的交易hash,转出数量
153 | 为5000,对方账号以及地址。发行方即为我们的账号以及地址。
154 | ```
155 | ```
156 | 账户2交易详情中能够看到资产名称为比特币,资产类型为转入,资产余额为账户1中转入的5000,资产地址
157 | 为资产在合约上的地址,发行方地址为账户1的地址,还要单位以及资产描述等信息。
158 | ```
159 | ```
160 | abi采用以下方式,主要使用了源码中的交易(Transfer)的方法,调用交易的账号会减少相关的金额,目标账户增加相应的金额,使用以下abi生成对应的payload接下来调用合约(invokecontract)完成交易
161 | var theAbi = {
162 | "constant": false,
163 | "inputs": [{"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"}],
164 | "name": "transfer",
165 | "outputs": [],
166 | "payable": false,
167 | "type": "function"
168 | };
169 |
170 | var methodParams = [to, amount];
171 | var encodedparams = encodeMethodParams(theAbi, methodParams);
172 | ...
173 | request({
174 | url: "https://api.hyperchain" + "/v1/dev/contract/invoke",
175 | method: "POST",
176 | json: true,
177 | headers: {
178 | "content-type": "application/json",
179 | "Accept": "application/json",
180 | "Authorization": accessToken
181 | }
182 | , body: requestData
183 | }
184 | ```
185 | * 注册账户
186 | 
187 | * 账户1发起交易
188 | 
189 | * 账户1发起交易详情
190 | 
191 | * 账户2接收交易详情
192 | 
193 |
194 | ## 资产列表
195 | ```
196 | 资产列表中能够看到账户的全部发行资产以及交易等信息点击进去可以看到交易详情。
197 | ```
198 | * 账户1资产列表
199 | 
200 | * 账户2资产列表
201 | 
202 | * 账户1交易记录
203 | 
204 | * 账户2交易记录
205 | 
206 |
--------------------------------------------------------------------------------
/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 | //routes
9 | var routes = require('./routes/index').router;
10 | var token = require('./routes/token');
11 | var User = require('./routes/Users');
12 | var assets = require('./routes/asset');
13 | var transaction = require('./routes/transaction');
14 | var password = require('./routes/password');
15 |
16 | var mongoose = require('mongoose');
17 | var Users = require('./models/Users').Users;
18 | var authToken = require('./models/Users').authToken;
19 | var app = express();
20 |
21 | //connect to database
22 | mongoose.connect('mongodb://127.0.0.1/blockchain');
23 | //mongoose.connect('mongodb://118.31.187.17/blockchain');
24 | //mongoose.connect('mongodb://139.129.44.55/blockchain');
25 | // view engine setup
26 | app.set('views', path.join(__dirname, 'ng_app'));
27 | app.set('view engine', 'jade');
28 |
29 | // uncomment after placing your favicon in /public
30 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
31 | app.use(logger('dev'));
32 | app.use(bodyParser.json());
33 | app.use(bodyParser.urlencoded({ extended: false }));
34 | app.use(cookieParser());
35 | app.use(express.static(path.join(__dirname, 'ng_app')));
36 | app.use(express.static(path.join(__dirname, '')));
37 |
38 | // We are going to protect /api routes with JWT
39 | app.use('/',token);
40 | app.use('/', routes);
41 | app.use('/',User);
42 | app.use('/asset', authToken, assets);
43 | app.use('/transaction', authToken, transaction);
44 | app.use('/password',password);
45 | // catch 404 and forward to error handler
46 | app.use(function(req, res, next) {
47 | var err = new Error('Not Found');
48 | err.status = 404;
49 | next(err);
50 | });
51 |
52 | // error handlers
53 |
54 | // development error handler
55 | // will print stacktrace
56 | if (app.get('env') === 'development') {
57 | app.use(function(err, req, res) {
58 | res.status(err.status || 500);
59 | res.send({"status":"error"});
60 | });
61 | }
62 |
63 | // production error handler
64 | // no stacktraces leaked to user
65 | app.use(function(err, req, res) {
66 | res.status(err.status || 500);
67 | res.send({"status":"error"});
68 | });
69 |
70 | module.exports = app;
71 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('web:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 |
81 | /**
82 | * Event listener for HTTP server "listening" event.
83 | */
84 |
85 | function onListening() {
86 | var addr = server.address();
87 | var bind = typeof addr === 'string'
88 | ? 'pipe ' + addr
89 | : 'port ' + addr.port;
90 | debug('Listening on ' + bind);
91 | }
92 |
--------------------------------------------------------------------------------
/blockchain_lib/conf.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | client_id: 'APPKEY STRING',
3 | client_secret: 'APPSECRET STRING',
4 | phone: 'HYPERCHAIN ACCOUNT',
5 | password: 'PASSWORD'
6 | }
--------------------------------------------------------------------------------
/blockchain_lib/lib.js:
--------------------------------------------------------------------------------
1 | var Web3 = require('hpc-web3');
2 | var SolidityFunction = require('hpc-web3/lib/web3/function');
3 | var ethereumUtil = require('ethereumjs-util');
4 | var _ = require("lodash");
5 | var request = require('request');
6 | var coder = require('hpc-web3/lib/solidity/coder');
7 | var conf = require('./conf')
8 |
9 | const secp256k1 = require('secp256k1');
10 | const TOKEN_ABI = '[{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"issue","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Issue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Transfer","type":"event"}]'
11 | const TOKEN_SOURCE = 'contract Token { address issuer; mapping (address => uint) balances; event Issue(address account, uint amount); event Transfer(address from, address to, uint amount); function Token() { issuer = msg.sender; } function issue(address account, uint amount) { if (msg.sender != issuer) throw; balances[account] += amount; } function transfer(address to, uint amount) { if (balances[msg.sender] < amount) throw; balances[msg.sender] -= amount; balances[to] += amount; Transfer(msg.sender, to, amount); } function getBalance(address account) constant returns (uint) { return balances[account]; } }';
12 | const TOKEN_BIN = '0x6060604052341561000c57fe5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101ca806100396000396000f300606060405263ffffffff60e060020a600035041663867904b48114610037578063a9059cbb14610058578063f8b2cb4f14610079575bfe5b341561003f57fe5b610056600160a060020a03600435166024356100a7565b005b341561006057fe5b610056600160a060020a03600435166024356100e6565b005b341561008157fe5b610095600160a060020a036004351661017f565b60408051918252519081900360200190f35b60005433600160a060020a039081169116146100c35760006000fd5b600160a060020a03821660009081526001602052604090208054820190555b5050565b600160a060020a0333166000908152600160205260409020548190101561010d5760006000fd5b600160a060020a0333811660008181526001602090815260408083208054879003905593861680835291849020805486019055835192835282015280820183905290517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360600190a15b5050565b600160a060020a0381166000908152600160205260409020545b9190505600a165627a7a72305820299e9bb6a492d60cb690d97c76ac26d821ff6bba1b863ce1b8720e449789692c0029'
13 | const URL_GTOKEN = 'https://api.hyperchain.cn/v1/token/gtoken'
14 |
15 | var options = {
16 | method: 'POST',
17 | url: URL_GTOKEN,
18 | formData:{
19 | client_id: conf.client_id,
20 | client_secret: conf.client_secret,
21 | phone: conf.phone,
22 | password: conf.password
23 | }
24 | };
25 |
26 | var newAsset = function (from, privkey, cb) {
27 |
28 | console.log(from)
29 | console.log("----------------")
30 | var account = from;
31 |
32 | var requestData = {
33 | "Bin": TOKEN_BIN,
34 | "From": account
35 | }
36 |
37 |
38 | request(options, function (error, response, body) {
39 | if (error) throw new Error(error);
40 | var obj = JSON.parse(body)
41 | var accessToken = obj['access_token'];
42 |
43 | console.log(accessToken)
44 | request({
45 | url: "https://api.hyperchain.cn" + "/v1/dev/contract/deploy",
46 | method: "POST",
47 | json: true,
48 | headers: {
49 | "content-type": "application/json",
50 | "Accept": "application/json",
51 | "Authorization": accessToken
52 | }
53 | , body: requestData
54 | }, function (error, response, body) {
55 | if (!error && response.statusCode == 200) {
56 | console.log(body)
57 | console.log(body.TxHash)
58 | var hash = body.TxHash;
59 | console.log(hash)
60 | cb && checkForContractAddress(hash, cb);
61 | } else {
62 | console.error(error);
63 | }
64 | });
65 | });
66 | // cb && checkForContractAddress(hash, cb);
67 | }
68 |
69 | //invoke
70 | var encodeMethodParams = function (theAbi, params, cb) {
71 | var types = theAbi.inputs.map(function (input) {
72 | return input.type;
73 | });
74 | for (var i = 0; i < types.length; i++) {
75 | switch (types[i]) {
76 | case "bytes32[]":
77 | try {
78 | params[i] = JSON.parse(params[i]);
79 | } catch (e) {
80 | if (typeof cb == 'function') {
81 | cb(new Error("param " + param[i] + " not match the format of type bytes32[]"))
82 | }
83 | }
84 | if (params[i] instanceof Array) {
85 | for (var j = 0; j < params[i].length; j++) {
86 | params[i][j] += ""
87 | }
88 | }
89 | //add others case to here
90 | }
91 | }
92 | return coder.encodeParams(types, params);
93 | };
94 |
95 | var issueAsset = function (from, privkey, address, amount, cb) {
96 | console.log("from:" + from + "address" + address)
97 | var theAbi = {
98 | "constant": false,
99 | "inputs": [{"name": "account", "type": "address"}, {"name": "amount", "type": "uint256"}],
100 | "name": "issue",
101 | "outputs": [],
102 | "payable": false,
103 | "type": "function"
104 | };
105 | var methodParams = [from, amount];
106 | var encodedparams = encodeMethodParams(theAbi, methodParams);
107 | var fun = new SolidityFunction('', theAbi, '');
108 | var payloadData = '0x' + fun.signature() + encodedparams;
109 |
110 | console.log(payloadData)
111 | var hash = "";
112 | var requestData = {
113 | "Const": true,
114 | "From": from,
115 | "Payload": payloadData,
116 | "To": address
117 | }
118 | request(options, function (error, response, body) {
119 | if (error) throw new Error(error);
120 |
121 | var obj = JSON.parse(body)
122 | var accessToken = obj['access_token'];
123 |
124 | console.log(accessToken)
125 | request({
126 | url: "https://api.hyperchain.cn" + "/v1/dev/contract/invoke",
127 | method: "POST",
128 | json: true,
129 | headers: {
130 | "content-type": "application/json",
131 | "Accept": "application/json",
132 | "Authorization": accessToken
133 | }
134 | , body: requestData
135 | }, function (error, response, body) {
136 | if (!error && response.statusCode == 200) {
137 | console.log(body)
138 | console.log(body.TxHash)
139 | hash = body.TxHash;
140 | cb && checkForContractTransaction(hash, cb)
141 | } else {
142 |
143 | console.log(response.statusCode);
144 |
145 | }
146 | });
147 | });
148 |
149 |
150 | }
151 |
152 | var random_16bits = function () {
153 | var num = Math.random().toString();
154 | if (num.substr(num.length - 16, 1) === '0' || num.length <= 17) {
155 | return random_16bits();
156 | }
157 | return num.substring(num.length - 16);
158 | }
159 |
160 | var getTimestamp = function (time) {
161 | if (_.isUndefined(time) || _.isNull(time)) {
162 | return Math.round(new Date().getTime() / 1000);
163 | } else {
164 | return Math.round(new Date(time).getTime() / 1000);
165 | }
166 | };
167 |
168 | var checkForContractTransaction = function (hash, callback) {
169 | // wait for receipt
170 | var flag = false;
171 | var startTime = new Date().getTime();
172 | var getResp = function () {
173 | if (!flag) {
174 | if ((new Date().getTime() - startTime) < 8000) {
175 | request(options, function (error, response, body) {
176 | if (error) throw new Error(error);
177 |
178 | var obj = JSON.parse(body)
179 | var accessToken = obj['access_token'];
180 |
181 | console.log(accessToken)
182 | var receipt = request({
183 | url: "https://api.hyperchain.cn" + "/v1/dev/transaction/txreceipt" + "?txhash=" + hash,
184 | method: "GET",
185 | json: true,
186 | headers: {
187 | "Accept": "Accept: text/html",
188 | "Authorization": accessToken
189 | }
190 | },
191 | function (err, receipt) {
192 | if (receipt && receipt.body.TxHash) {
193 | console.log("----" + receipt.body.TxHash)
194 | console.log(receipt.body)
195 | var temp = {
196 | status: receipt.body.Status,
197 | txHash: receipt.body.TxHash,
198 | postState: receipt.body.PostState,
199 | contractAddress: receipt.body.ContractAddress,
200 | ret: receipt.body.Ret
201 | }
202 |
203 | callback(null, temp);
204 | } else {
205 | setTimeout(getResp, 400);
206 | }
207 | });
208 | });
209 | } else {
210 | callback(new Error("getTransactionReceipt timeout..."));
211 | }
212 | }
213 | };
214 | setTimeout(getResp, 400);
215 | };
216 |
217 | var checkForContractAddress = function (hash, callback) {
218 | // wait for receipt
219 | var flag = false;
220 | var startTime = new Date().getTime();
221 | var getResp = function () {
222 | if (!flag) {
223 | if ((new Date().getTime() - startTime) < 8000) {
224 | var t1 = getTimestamp();
225 |
226 | request(options, function (error, response, body) {
227 | if (error) throw new Error(error);
228 |
229 | var obj = JSON.parse(body)
230 | var accessToken = obj['access_token'];
231 |
232 | console.log(accessToken)
233 | request({
234 |
235 | url: "https://api.hyperchain.cn" + "/v1/dev/transaction/txreceipt" + "?txhash=" + hash,
236 | method: "GET",
237 | json: true,
238 | headers: {
239 | "Accept": "Accept: text/html",
240 | "Authorization": accessToken
241 | }
242 | },
243 | function (err, receipt) {
244 | // if (receipt && receipt != undefined) {
245 | if (receipt.body.ContractAddress != undefined && receipt.body.ContractAddress != "") {
246 | flag = true;
247 | console.log(receipt.body)
248 | console.log(receipt.body.ContractAddress)
249 |
250 | callback && callback(null, receipt.body.ContractAddress);
251 | } else {
252 | console.log("resend after 50");
253 | setTimeout(getResp, 50)
254 | }
255 | });
256 | });
257 | var t2 = getTimestamp();
258 |
259 | } else {
260 | callback(new Error("getTransactionReceipt timeout..."));
261 | }
262 | }
263 | };
264 | getResp()
265 | };
266 |
267 | var newTransaction = function (from, to, amount, privKey, contractAddr, cb) {
268 |
269 | var theAbi = {
270 | "constant": false,
271 | "inputs": [{"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"}],
272 | "name": "transfer",
273 | "outputs": [],
274 | "payable": false,
275 | "type": "function"
276 | };
277 |
278 | var methodParams = [to, amount];
279 | var encodedparams = encodeMethodParams(theAbi, methodParams);
280 | var fun = new SolidityFunction('', theAbi, '');
281 | var payloadData = '0x' + fun.signature() + encodedparams;
282 |
283 | console.log(payloadData)
284 | var hash = "";
285 | var requestData = {
286 | "Const": true,
287 | "From": from,
288 | "Payload": payloadData,
289 | "To": to
290 | }
291 |
292 | request(options, function (error, response, body) {
293 | if (error) throw new Error(error);
294 |
295 | var obj = JSON.parse(body)
296 | var accessToken = obj['access_token'];
297 |
298 | console.log(accessToken)
299 | request({
300 | url: "https://api.hyperchain.cn" + "/v1/dev/contract/invoke",
301 | method: "POST",
302 | json: true,
303 | headers: {
304 | "content-type": "application/json",
305 | "Accept": "application/json",
306 | "Authorization": accessToken
307 | }
308 | , body: requestData
309 | }, function (error, response, body) {
310 | if (!error && response.statusCode == 200) {
311 | console.log(body)
312 | console.log(body.TxHash)
313 | hash = body.TxHash;
314 | cb && checkForContractTransaction(hash, cb)
315 | } else {
316 |
317 | console.log(response.statusCode);
318 |
319 | }
320 | });
321 | });
322 |
323 | }
324 |
325 | module.exports = {
326 | checkForContractAddress: checkForContractAddress,
327 | newAsset: newAsset,
328 | getTimestamp: getTimestamp,
329 | random_16bits: random_16bits,
330 | checkForContractTransaction: checkForContractTransaction,
331 | issueAsset: issueAsset,
332 | newTransaction: newTransaction
333 | }
334 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ether-admin",
3 | "description": "",
4 | "main": "app.js",
5 | "license": "MIT",
6 | "homepage": "index.html",
7 | "dependencies": {
8 | "angular": "^1.5.5",
9 | "angular-route": "^1.5.5",
10 | "bootstrap": "^3.3.6",
11 | "ng-file-upload-shim": "^12.0.4",
12 | "angular-bootstrap": "^1.3.3",
13 | "angular-base64": "^2.0.5",
14 | "ng-img-crop": "ngImgCrop#^0.3.2",
15 | "angular-loading-bar": "^0.9.0",
16 | "font-awesome-animation": "^0.0.9"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/help_utils/base64_util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Tyrion on 2016/5/26.
3 | */
4 | module.exports.decode = function(string){
5 | return new Buffer(string,'base64').toString();
6 | };
--------------------------------------------------------------------------------
/help_utils/geth_level_utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Tyrion on 2016/6/8.
3 | */
4 | var BASE_URL = 'http://10.214.20.118:3000';
5 | module.exports.BASE_URL = BASE_URL;
--------------------------------------------------------------------------------
/imgs/创建资产.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/创建资产.png
--------------------------------------------------------------------------------
/imgs/发行资产.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/发行资产.png
--------------------------------------------------------------------------------
/imgs/发行资产详情.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/发行资产详情.png
--------------------------------------------------------------------------------
/imgs/注册.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/注册.png
--------------------------------------------------------------------------------
/imgs/登录.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/登录.png
--------------------------------------------------------------------------------
/imgs/账户1交易记录.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户1交易记录.png
--------------------------------------------------------------------------------
/imgs/账户1信息.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户1信息.png
--------------------------------------------------------------------------------
/imgs/账户1发起交易.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户1发起交易.png
--------------------------------------------------------------------------------
/imgs/账户1发起交易详情.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户1发起交易详情.png
--------------------------------------------------------------------------------
/imgs/账户1资产列表.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户1资产列表.png
--------------------------------------------------------------------------------
/imgs/账户2交易记录.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户2交易记录.png
--------------------------------------------------------------------------------
/imgs/账户2信息.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户2信息.png
--------------------------------------------------------------------------------
/imgs/账户2接收交易详情.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户2接收交易详情.png
--------------------------------------------------------------------------------
/imgs/账户2资产列表.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/imgs/账户2资产列表.png
--------------------------------------------------------------------------------
/models/Users.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Created by Tyrion on 2016/5/25.
4 | */
5 | var mongoose = require('mongoose');
6 | var jwt = require('jwt-simple');
7 | var base64_util = require("../help_utils/base64_util");
8 |
9 | var Schema = mongoose.Schema;
10 | var _User = new Schema({
11 | phone : { type: String, required: true, unique: true },
12 | password : { type: String, required: true},
13 | account_addr : { type: String, required: true},
14 | private_key : {type: String, required: true},
15 | create_time : { type: Date, default: Date.now },
16 | assets:{type: Array},
17 | key: {type: String,required:true,default:"aVeryHardGuessString"}
18 | });
19 | //Password verification
20 | _User.methods.comparePassword = function(password, cb) {
21 | if(password == this.password){
22 | return cb(true);
23 | }
24 | return cb(false);
25 | };
26 | _User.methods.toJson = function(){
27 | return {
28 | phone: this.phone,
29 | create_time: this.create_time.toLocaleString(),
30 | account_addr: this.account_addr
31 | }
32 | };
33 | var Users = mongoose.model('User', _User);
34 |
35 |
36 | //A middleware to verify User token
37 | /**
38 | * When use this middleware ,your code should gose like this:
39 | * app.get('/something', authToken, function(req, res){
40 | * // do something
41 | * });
42 | * @param req
43 | * @param res
44 | * @param next
45 | * @returns {*}
46 | */
47 | var authToken = function(req, res, next) {
48 | // code goes here
49 | var authSequence = req.headers['authorization'];
50 | if(!authSequence){
51 | return res.status(401).send({
52 | status: 'failed',
53 | msg: "Token is required"
54 | });
55 | }
56 | var segments = authSequence.split(' ');
57 | if (segments.length !== 2) {
58 | throw new Error('Not enough or too many authorization segments');
59 | }
60 | var pass_secret = base64_util.decode(segments[1]).split(":");
61 | if (pass_secret.length !== 2) {
62 | throw new Error('Not enough or too many pass_secret segments');
63 | }
64 | var token = pass_secret[0];
65 | if (token) {
66 | try {
67 | var decoded = jwt.decode(token, 'jwtTokenSecret');
68 | // handle token here
69 | if (decoded.exp <= Date.now()) {
70 | return res.status(401).send({
71 | status: 'failed',
72 | msg:'Access token has expired'
73 | });
74 | }
75 | Users.findOne({ _id: decoded.id }, function(err, user) {
76 | if(err){
77 | return res.status(401).send({
78 | status: 'failed',
79 | msg: "cannot find user,mongo error."
80 | });
81 | }
82 | if(!user){
83 | return res.status(401).send({
84 | status: 'failed',
85 | msg: "cannot find user"
86 | });
87 |
88 | }
89 | if(decoded.password != user.password){
90 | return res.status(401).send({
91 | status: 'failed',
92 | msg: "token已失效,(token过期,密码修改可能导致这个问题),请重新登陆"
93 | })
94 | }
95 | req.user = user;
96 | return next();
97 | });
98 | } catch (err) {
99 | console.log(err);
100 | return res.status(401).send({
101 | status: 'failed',
102 | msg: "cannot find user,catch error"
103 | });
104 | }
105 | } else {
106 | console.log("6");
107 | return res.status(401).send({
108 | status: 'failed',
109 | msg:"I need a token!"
110 | });
111 | }
112 | };
113 | var verify_psw = function(req,res,next){
114 | // console.log("==========="+req.body);
115 | var psw = req.body.psw;
116 | if(psw){
117 | if(!req.user){
118 | return res.status(401).send({
119 | status: "refuse",
120 | msg: "token错误"
121 | })
122 | }
123 | req.user.comparePassword(psw, function(result){
124 | if(result){
125 | return next();
126 | }else{
127 | return res.status(401).send({
128 | status: "refuse",
129 | msg: "请输入正确的密码"
130 | })
131 | }
132 | })
133 | }else{
134 | return res.status(401).send({
135 | status: "refuse",
136 | msg: "请输入密码"
137 | })
138 | }
139 | };
140 | module.exports.authToken = authToken;
141 | module.exports.Users = Users;
142 | module.exports.verify_psw = verify_psw;
143 |
--------------------------------------------------------------------------------
/models/asset.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Vampire on 2016/5/27.
3 | */
4 | var mongoose = require('mongoose');
5 | var jwt = require('jwt-simple');
6 | var base64_util = require("../help_utils/base64_util");
7 |
8 | var Schema = mongoose.Schema;
9 |
10 | var ASSET_TYPE={
11 | RECEIVING:"转入",
12 | ISSUE:"发行"
13 | };
14 | var _Asset = new Schema({
15 | name : { type: String, required: true
16 | // , unique: true TODO:资产名可以不唯一
17 | },
18 | amount : { type: Number, required: true, default: 0},
19 | balance: {type: Number, required: true, default: 0},
20 | unit : { type: String, required: true},
21 | description : { type: String, required: true},
22 | logo : { type: String, required: true},
23 | account_addr : { type: String, required: true},
24 | create_time : { type: Date, default: Date.now },
25 | asset_addr: {type: String, required: true},
26 | holder: {type: Array},
27 | type: {type:String, required:true, default:ASSET_TYPE.ISSUE}
28 | });
29 | _Asset.methods.toJson = function(){
30 | return {
31 | name: this.name,
32 | amount: this.amount,
33 | balance: this.balance,
34 | unit: this.unit,
35 | description: this.description,
36 | logo: this.logo,
37 | type: this.type,
38 | account_addr: this.account_addr,
39 | create_time: this.create_time.toLocaleString(),
40 | asset_addr: this.asset_addr
41 | }
42 | };
43 | var Assets = mongoose.model('Asset', _Asset);
44 |
45 | module.exports.Assets = Assets;
46 | module.exports.ASSET_TYPE = ASSET_TYPE;
47 |
--------------------------------------------------------------------------------
/models/transaction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Vampire on 2016/5/27.
3 | */
4 | var mongoose = require('mongoose');
5 | var ASSET_TYPE = require('./asset').ASSET_TYPE;
6 | var Asset = require('./asset').Assets;
7 | var Users = require('./Users').Users;
8 |
9 | var Schema = mongoose.Schema;
10 |
11 | var TRANSACTION_TYPE={
12 | TRANSACTION:"交易",
13 | ISSUE:"发行"
14 | };
15 | var _Transaction = new Schema({
16 | hash: { type: String, required: true, unique:true},
17 | from : { type: String, required: true, default:"null"},
18 | amount : { type: Number, required: true, default: 0},
19 | to : { type: String, required: true, default:"null"},
20 | asset_addr : { type: String, required: true},
21 | create_time : { type: Date, default: Date.now },
22 | type : {type :String, required: true, default: TRANSACTION_TYPE.TRANSACTION}
23 | });
24 |
25 | _Transaction.methods.toJson = function(callback){
26 | var tr = this;
27 | Asset.findOne({asset_addr:this.asset_addr,type:ASSET_TYPE.ISSUE}, function (err, origin_asset) {
28 | Asset.findOne({asset_addr: tr.asset_addr},function(err,asset){
29 | Users.findOne({account_addr:origin_asset.account_addr},function(err,issuer){
30 |
31 | callback(err,{
32 | hash:tr.hash,
33 | from: tr.from,
34 | amount: tr.amount,
35 | to: tr.to,
36 | asset_addr: tr.assets_addr,
37 | asset_name: asset?asset.name:"无",
38 | issuer: issuer?{
39 | phone:issuer.phone,
40 | account_addr:issuer.account_addr
41 | }:{},
42 | create_time: tr.create_time.toLocaleString()
43 | });
44 | })
45 | })
46 | });
47 | };
48 | var Transaction = mongoose.model('Transaction', _Transaction);
49 |
50 | module.exports.Transaction = Transaction;
51 | module.exports.TRANSACTION_TYPE = TRANSACTION_TYPE;
52 |
--------------------------------------------------------------------------------
/ng_app/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "cwd": "..",
3 | "directory": "ng_app/bower_components"
4 | }
5 |
--------------------------------------------------------------------------------
/ng_app/css/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
5 |
--------------------------------------------------------------------------------
/ng_app/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/ng_app/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/ng_app/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/ng_app/img/aif.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ng_app/img/group-logo.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/ng_app/img/group.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/ng_app/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/img/logo.png
--------------------------------------------------------------------------------
/ng_app/img/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/img/logo2.png
--------------------------------------------------------------------------------
/ng_app/img/logo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/img/logo3.png
--------------------------------------------------------------------------------
/ng_app/img/logo4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperchaincn/Digital-Wallet/e2cef1587aa6e34c3bb16149bc2119770237a711/ng_app/img/logo4.png
--------------------------------------------------------------------------------
/ng_app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 浙大区块链实验室
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
45 |
--------------------------------------------------------------------------------
/ng_app/js/controllers.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('myApp');
2 |
3 | app.controller('WhiteController', function($scope, $http, $window) {
4 | $http({
5 | method: 'GET',
6 | url: '/user'
7 | }).then(function successCallback(response) {
8 | if (response.status == 200) {
9 | $window.location.href = '/dashboard';
10 | }
11 | }).then(function errorCallback(response) {});
12 | });
13 |
14 | app.controller('DashboardController', function($scope, $route, $window) {
15 | $scope.$route = $route;
16 | $scope.account = {
17 | phone: ''
18 | };
19 | $scope.signOut = function($scope) {
20 | delete $window.sessionStorage.token;
21 | $window.location.href = '/login';
22 | };
23 | });
24 |
25 | // ------------------------------账户---------------------------------------
26 | app.controller('UserController', function($scope, $http, $route, $uibModal) {
27 | $scope.$route = $route;
28 | $http({
29 | method: 'GET',
30 | url: '/user'
31 | }).then(function(result) {
32 | data = result.data
33 | $scope.account.phone = data.data.phone;
34 | $scope.user = {
35 | account: data.data.phone,
36 | address: data.data.account_addr,
37 | time: data.data.create_time
38 | };
39 | },function(data, status, headers, config) {});
40 |
41 | $scope.open = function(size) {
42 | var modalInstance = $uibModal.open({
43 | templateUrl: 'views/verifyPwd.html',
44 | controller: 'KeyPwdModalCtrl',
45 | size: size,
46 | resolve: {
47 | title: function() {
48 | return '查看密钥';
49 | },
50 | httpArgs: function() {
51 | return {
52 | method: 'POST',
53 | url: '/key'
54 | };
55 | }
56 | }
57 | });
58 | modalInstance.result.then(function(key) {
59 | $scope.user.key = key;
60 | });
61 | };
62 |
63 | $scope.changePwd = function() {
64 | var modalInstance = $uibModal.open({
65 | templateUrl: 'views/changePwd.html',
66 | controller: 'changePwdCtrl',
67 | size: 'md'
68 | });
69 | };
70 | });
71 |
72 | app.controller('changePwdCtrl', function($scope, $uibModalInstance, $http, $window) {
73 | $scope.ok = function() {
74 | console.log($scope.psw);
75 | if ($scope.psw.new_psw != $scope.repeat_psw) {
76 | $scope.repeat_error = '重复新密码错误!';
77 | return;
78 | } else {
79 | delete $scope.repeat_error;
80 | }
81 | $http({
82 | method: 'PUT',
83 | url: '/password',
84 | data: $scope.psw
85 | }).then(function(response) {
86 | if (response.status == 200) {
87 | $uibModalInstance.close();
88 | delete $window.sessionStorage.token;
89 | $window.location.href = '/login';
90 | } else {
91 | $scope.error = response.data.msg;
92 | }
93 | });
94 | };
95 |
96 | $scope.close = function() {
97 | $uibModalInstance.close();
98 | };
99 | });
100 |
101 | app.controller('KeyPwdModalCtrl', function($scope, $uibModalInstance, $http, title, httpArgs) {
102 | var key = "default";
103 | $scope.title = title;
104 | $scope.ok = function() {
105 | $http(httpArgs).then(function() {
106 | key = "privbvqVf51x29pE79Sr2HDiasGqnUDQU9zRUWwEbRcdaPZhgFqXDP6Z";
107 | $uibModalInstance.close(key);
108 | }, function() {
109 | key = "privbvqVf51x29pE79Sr2HDiasGqnUDQU9zRUWwEbRcdaPZhgFqXDP6Z";
110 | $uibModalInstance.close(key);
111 | });
112 | };
113 | });
114 |
115 | //--------------------------------资产--------------------------------------------------
116 | app.controller('CreateAssetController', function($scope, Upload, $timeout, $http, $uibModal) {
117 | // $scope.create = function(file) {
118 | // console.log(file);
119 | // console.log($scope.asset);
120 | // if (file) {
121 | // file.upload = Upload.upload({
122 | // url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
123 | // data: {
124 | // file: file,
125 | // asset: $scope.asset
126 | // }
127 | // });
128 | // file.upload.then(function(response) {
129 | // $timeout(function() {
130 | // file.result = response.data;
131 | // });
132 | // }, function(response) {
133 | // if (response.status > 0)
134 | // $scope.errorMsg = response.status + ': ' + response.data;
135 | // }, function(evt) {
136 | // file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
137 | // });
138 | // }
139 | // };
140 |
141 | // $scope.cropped = false;
142 | // $scope.logo = false;
143 |
144 | // $scope.selectImg = function(){
145 | // console.log($scope.logo);
146 | // console.log($scope.cropped);
147 | // if($scope.cropped){
148 | // $scope.cropped = !$scope.cropped;
149 | // }
150 | // }
151 |
152 | $scope.create = function(dataUrl, name) {
153 | var modalInstance = $uibModal.open({
154 | templateUrl: 'views/verifyPwd.html',
155 | controller: 'DetailModalInstanceCtrl',
156 | size: 'sm',
157 | backdrop: 'static',
158 | resolve: {
159 | title: function() {
160 | return "创建资产";
161 | },
162 | httpArgs: function() {
163 | return {
164 | method: 'POST',
165 | url: '/asset',
166 | data: $scope.asset
167 | };
168 | },
169 | url: function() {
170 | return '/asset/detail';
171 | }
172 | }
173 | });
174 | };
175 | });
176 |
177 | app.controller('CreateAssetModalInstanceCtrl', function($scope, $http, $uibModalInstance, $location, Upload, data, file, DetailService, $window, $base64) {
178 | $scope.title = '创建资产';
179 | $scope.ok = function() {
180 | data.psw = $scope.psw;
181 | Upload.http({
182 | url: '/asset',
183 | data: {
184 | data: data,
185 | file: file
186 | }
187 | })
188 | // $http({
189 | // method: 'POST',
190 | // url: '/asset',
191 | // data: data
192 | // })
193 | .then(function(response) {
194 | DetailService.setDetail(response.data.data);
195 | $uibModalInstance.close();
196 | $location.url('/asset/detail');
197 | }, function(response) {
198 | $scope.error = "密码错误!";
199 | });
200 | };
201 | });
202 |
203 | app.controller('IssueAssetController', function($scope, $http, $uibModal) {
204 | $http({
205 | method: 'GET',
206 | url: '/asset?type=issue',
207 | }).then(function(response) {
208 | $scope.items = response.data.data;
209 | });
210 |
211 | $scope.changeIssueAsset = function(asset) {
212 | $scope.issue = {
213 | asset_addr: asset.asset_addr
214 | };
215 | };
216 |
217 | $scope.doIssue = function() {
218 | var modalInstance = $uibModal.open({
219 | templateUrl: 'views/verifyPwd.html',
220 | controller: 'DetailModalInstanceCtrl',
221 | size: 'sm',
222 | backdrop: 'static',
223 | resolve: {
224 | title: function() {
225 | return '发行资产';
226 | },
227 | httpArgs: function() {
228 | return {
229 | method: 'PUT',
230 | url: '/asset',
231 | data: $scope.issue
232 | };
233 | },
234 | url: function() {
235 | return '/asset/detail';
236 | }
237 | }
238 | });
239 | };
240 | });
241 |
242 | function down(x, y) {
243 | return (x.create_time < y.create_time) ? 1 : -1;
244 | }
245 |
246 | function getDetail($scope, $http, url) {
247 | $http({
248 | method: 'GET',
249 | url: url
250 | }).then(function(response) {
251 | var items = response.data.data;
252 | items.sort(down);
253 | $scope.totalItems = items.length;
254 | $scope.perPage = 10;
255 | $scope.items = items.slice(0, $scope.perPage);
256 | $scope.pageChanged = function() {
257 | $scope.items = items.slice(($scope.perPage * ($scope.currentPage - 1)), ($scope.perPage * $scope.currentPage)); //通过当前页数筛选出表格当前显示数据
258 | };
259 | });
260 | }
261 |
262 | app.controller('AssetListController', function($scope, $http, $location, DetailService) {
263 | $scope.selectedType = "all";
264 | getDetail($scope, $http, '/asset');
265 |
266 | $scope.changeType = function(selectedType) {
267 | if (selectedType == 'in') {
268 | getDetail($scope, $http, '/asset?type=receive');
269 | } else if (selectedType == 'issue') {
270 | getDetail($scope, $http, '/asset?type=issue');
271 | } else if (selectedType == 'all') {
272 | getDetail($scope, $http, '/asset');
273 | }
274 | };
275 |
276 | $scope.detail = function(asset) {
277 | DetailService.setDetail(asset);
278 | $location.url('/asset/detail');
279 | };
280 | });
281 |
282 | app.controller('AssetDetailController', function($scope, DetailService) {
283 | $scope.asset = DetailService.getDetail();
284 | });
285 |
286 | //-----------------------------------交易---------------------------------
287 | app.controller('AssetTransferController', function($scope, $uibModal, $http) {
288 | $http({
289 | method: 'GET',
290 | url: '/asset',
291 | }).then(function(response) {
292 | $scope.assets = response.data.data;
293 | });
294 |
295 | $scope.changeAsset = function(asset) {
296 | $scope.trx = {
297 | asset_addr: asset.asset_addr
298 | };
299 | };
300 |
301 | $scope.setBalance = function(asset) {
302 | if (asset) {
303 | $scope.balance = asset.balance;
304 | }
305 | };
306 |
307 | $scope.transfer = function() {
308 | if ($scope.trx.amount > $scope.balance) {
309 | $scope.balanceError = '余额不足';
310 | return;
311 | } else {
312 | delete $scope.balanceError;
313 | }
314 | var modalInstance = $uibModal.open({
315 | templateUrl: 'views/verifyPwd.html',
316 | controller: 'DetailModalInstanceCtrl',
317 | size: 'sm',
318 | backdrop: 'static',
319 | resolve: {
320 | title: function() {
321 | return '转出资产';
322 | },
323 | httpArgs: function() {
324 | return {
325 | method: 'POST',
326 | url: '/transaction',
327 | data: $scope.trx
328 | };
329 | },
330 | url: function() {
331 | return '/transaction/detail';
332 | }
333 | }
334 | });
335 | };
336 | });
337 |
338 | app.controller('TransactionListController', function($scope, $uibModal, $http, DetailService, $location) {
339 | $scope.selectedType = "all";
340 | getDetail($scope, $http, '/transaction');
341 |
342 | $scope.changeType = function(selectedType) {
343 | if (selectedType == 'all') {
344 | getDetail($scope, $http, '/transaction');
345 | } else if (selectedType == 'in') {
346 | getDetail($scope, $http, 'transaction?type=receive');
347 | } else if (selectedType == 'out') {
348 | getDetail($scope, $http, 'transaction?type=transfer');
349 | } else if (selectedType == 'issue') {
350 | getDetail($scope, $http, 'transaction?type=issue');
351 | }
352 | };
353 |
354 | $scope.detail = function(record) {
355 | DetailService.setDetail(record);
356 | $location.url('/transaction/detail');
357 | };
358 | });
359 |
360 | app.controller('TransactionDetailController', function($scope, DetailService) {
361 | $scope.trx = DetailService.getDetail();
362 | });
363 |
364 | app.controller('DetailModalInstanceCtrl', function($scope, $uibModalInstance, $http, title, httpArgs, DetailService, $location, url) {
365 | $scope.title = title;
366 | $scope.disappear = false;
367 | $scope.ok = function() {
368 | $scope.disappear = true;
369 | delete $scope.error;
370 | httpArgs.data.psw = $scope.psw;
371 | $http(httpArgs).then(function(response) {
372 | if (response.status == 200) {
373 | DetailService.setDetail(response.data.data);
374 | $uibModalInstance.close();
375 | $location.url(url);
376 | } else {
377 | $scope.disappear = false;
378 | $scope.error = response.data.msg;
379 | }
380 | }, function(response) {
381 | $scope.disappear = false;
382 | $scope.error = "密码错误!";
383 | });
384 | };
385 |
386 | $scope.close = function() {
387 | $uibModalInstance.close();
388 | };
389 | });
--------------------------------------------------------------------------------
/ng_app/js/directives.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('myApp');
2 | app.directive('my-file-input', ['', function() {
3 | // Runs during compile
4 | return {
5 | // name: '',
6 | // priority: 1,
7 | // terminal: true,
8 | // scope: {}, // {} = isolate, true = child, false/undefined = no change
9 | // controller: function($scope, $element, $attrs, $transclude) {},
10 | // require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements
11 | restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment
12 | template: "",
13 | // templateUrl: '',
14 | // replace: true,
15 | // transclude: true,
16 | // compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),
17 | link: function($scope, iElm, iAttrs, controller) {
18 | console.log("my-directive here");
19 | iElm.fileinput({
20 | language: "zh",
21 | // uploadUrl: "/file-upload-batch/2",
22 | allowedFileExtensions: ["jpg", "png", "gif"]
23 | });
24 | }
25 | };
26 | }]);
27 |
28 | app.directive('hello', function() {
29 | return {
30 | restrict: 'E',
31 | template: 'Hi there fsdaffffffffffffssdasfffffffffffff
',
32 | replace: true
33 | };
34 | });
--------------------------------------------------------------------------------
/ng_app/js/interceptors.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('myApp');
2 | app.factory('authInterceptor', function($q, $window, $location, $base64) {
3 | var interceptor = {
4 | 'request': function(config) {
5 | config.headers = config.headers || {};
6 | if ($window.sessionStorage.token) {
7 | config.headers.Authorization = 'Basic ' + $base64.encode($window.sessionStorage.token + ":");
8 | }else{
9 | // $location.url("/login");
10 | $window.location.href = "/login";
11 | }
12 | return config;
13 | },
14 | 'response': function(response) {
15 | response.config.responseTimestamp = new Date().getTime();
16 | return response || $q.when(response);
17 | },
18 | 'requestError': function(rejection) {
19 | return response;
20 | },
21 | 'responseError': function(response) {
22 | if (response.status === 401) {
23 | // $window.location.href = "/login";
24 | }
25 | return response;
26 | }
27 | };
28 | return interceptor;
29 | });
--------------------------------------------------------------------------------
/ng_app/js/loginAndRegisterModule.js:
--------------------------------------------------------------------------------
1 | var lar = angular.module('myLoginRegister', ['ngRoute']);
2 |
3 | lar.controller('LoginController', function($scope, $http, $location, $window) {
4 | $scope.login = function() {
5 | // console.log($.param($scope.loginFormData));
6 | $http({
7 | url: '/token',
8 | method: 'POST',
9 | data: $scope.loginFormData
10 | }).then(function(result) {
11 | $window.sessionStorage.token = result.data.token;
12 | $window.location.href = '/dashboard';
13 | },function(result) {
14 | $scope.loginError = "手机号或者密码错误!";
15 | })
16 | };
17 | });
18 |
19 | lar.controller('RegisterController', function($scope, $http, $location, $window) {
20 | $scope.register = function() {
21 | $http({
22 | method: 'POST',
23 | url: '/user',
24 | data: $scope.registerFormData
25 | }).then(function(result) {
26 | alert(JSON.stringify(result.data.data.token))
27 | $window.sessionStorage.token = result.data.data.token;
28 | $window.location.href = '/dashboard';
29 | },function(result) {
30 | $scope.registerError = result.msg;
31 | });
32 | };
33 | });
34 |
--------------------------------------------------------------------------------
/ng_app/js/menu.js:
--------------------------------------------------------------------------------
1 | //Menu JS
2 | (function($) {
3 |
4 | $.fn.bluemoonMenu = function(options) {
5 |
6 | var cssmenu = $(this),
7 | settings = $.extend({
8 | title: "Menu",
9 | format: "dropdown",
10 | sticky: false
11 | }, options);
12 |
13 | return this.each(function() {
14 | cssmenu.prepend('');
15 | var mainmenu;
16 | $(this).find("#menu-button").on('click', function() {
17 | $(this).toggleClass('menu-opened');
18 | mainmenu = $(this).next('ul');
19 | if (mainmenu.hasClass('open')) {
20 | mainmenu.hide().removeClass('open');
21 | } else {
22 | mainmenu.show().addClass('open');
23 | if (settings.format === "dropdown") {
24 | mainmenu.find('ul').show();
25 | }
26 | }
27 | });
28 |
29 | $(this).find("#asset").on('click', function() {
30 | if (mainmenu && mainmenu.hasClass('open')) {
31 | mainmenu.hide().removeClass('open');
32 | }
33 | });
34 |
35 | $(this).find("#trx").on('click', function() {
36 | if (mainmenu && mainmenu.hasClass('open')) {
37 | mainmenu.hide().removeClass('open');
38 | }
39 | });
40 |
41 | $(this).find("#user").on('click', function() {
42 | if (mainmenu && mainmenu.hasClass('open')) {
43 | mainmenu.hide().removeClass('open');
44 | }
45 | });
46 |
47 | cssmenu.find('li ul').parent().addClass('has-sub');
48 |
49 | multiTg = function() {
50 | cssmenu.find(".has-sub").prepend('');
51 | cssmenu.find('.submenu-button').on('click', function() {
52 | $(this).toggleClass('submenu-opened');
53 | if ($(this).siblings('ul').hasClass('open')) {
54 | $(this).siblings('ul').removeClass('open').hide();
55 | } else {
56 | $(this).siblings('ul').addClass('open').show();
57 | }
58 | });
59 | };
60 | if (settings.format === 'multitoggle') multiTg();
61 | else cssmenu.addClass('dropdown');
62 | if (settings.sticky === true) cssmenu.css('position', 'fixed');
63 | resizeFix = function() {
64 | if ($(window).width() > 768) {
65 | cssmenu.find('ul').show();
66 | }
67 | if ($(window).width() <= 768) {
68 | cssmenu.find('ul').hide().removeClass('open');
69 | }
70 | };
71 | resizeFix();
72 | return $(window).on('resize', resizeFix);
73 | });
74 | };
75 | })(jQuery);
76 |
77 | (function($) {
78 | $(document).ready(function() {
79 |
80 | $("#cssmenu").bluemoonMenu({
81 | title: "Menu",
82 | format: "multitoggle"
83 | });
84 | });
85 | })(jQuery);
--------------------------------------------------------------------------------
/ng_app/js/router.js:
--------------------------------------------------------------------------------
1 | // angular.module('myApp', ['ngRoute', 'myApp.controller', 'myApp.service', 'myApp.directive', 'myApp.interceptor'])
2 | var app = angular.module('myApp', ['ngRoute', 'ui.bootstrap', 'ngFileUpload', 'base64', 'ngImgCrop', 'angular-loading-bar']);
3 | app.config(['$routeProvider', '$locationProvider', '$httpProvider', 'cfpLoadingBarProvider', function($routeProvider, $locationProvider, $httpProvider, cfpLoadingBarProvider) {
4 | $routeProvider
5 | .when('/', {
6 | controller: 'WhiteController'
7 | })
8 | .when('/user', {
9 | templateUrl: '/views/user.html',
10 | controller: 'UserController',
11 | activetab: 'user'
12 | })
13 | .when('/asset/create', {
14 | templateUrl: '/views/assetCreate.html',
15 | controller: 'CreateAssetController',
16 | activetab: 'asset'
17 | })
18 | .when('/asset/issue', {
19 | templateUrl: '/views/assetIssue.html',
20 | controller: 'IssueAssetController',
21 | activetab: 'asset'
22 | })
23 | .when('/asset/list', {
24 | templateUrl: '/views/assetList.html',
25 | controller: 'AssetListController',
26 | activetab: 'asset'
27 | })
28 | .when('/asset/detail', {
29 | templateUrl: '/views/assetDetail.html',
30 | controller: 'AssetDetailController',
31 | activetab: 'asset'
32 | })
33 | .when('/transaction/list', {
34 | templateUrl: '/views/transactionList.html',
35 | controller: 'TransactionListController',
36 | activetab: 'transaction'
37 | })
38 | .when('/transaction/detail', {
39 | templateUrl: '/views/transactionDetail.html',
40 | controller: 'TransactionDetailController',
41 | activetab: 'transaction'
42 | })
43 | .when('/transfer', {
44 | templateUrl: '/views/transfer.html',
45 | controller: 'AssetTransferController',
46 | activetab: 'transaction'
47 | })
48 | .when('/test', {
49 | templateUrl: '/views/test.html',
50 | activetab: 'asset'
51 | })
52 | .when('/dashboard', {
53 | templateUrl: 'views/user.html',
54 | controller: 'UserController',
55 | activetab: 'user'
56 | })
57 | .otherwise({
58 | redirectTo: '/test'
59 | });
60 | $locationProvider.html5Mode(true);
61 | $httpProvider.interceptors.push('authInterceptor');
62 | cfpLoadingBarProvider.parentSelector = '#loading-bar-container';
63 | // cfpLoadingBarProvider.includeSpinner = false;
64 | cfpLoadingBarProvider.spinnerTemplate = ' 正在处理中...
';
65 |
66 | }]);
--------------------------------------------------------------------------------
/ng_app/js/services.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('myApp');
2 | app.factory('DetailService', function() {
3 | var detail = {};
4 | return {
5 | getDetail: function() {
6 | return this.detail;
7 | },
8 | setDetail: function(detail) {
9 | this.detail = detail;
10 | }
11 | };
12 | });
13 |
14 | app.factory('alertService', function($uibModal) {
15 | var alertService = {};
16 |
17 | // // 创建一个全局的 alert 数组
18 | // $rootScope.alerts = [];
19 |
20 | // alertService.add = function(type, msg) {
21 | // $rootScope.alerts.push({
22 | // 'type': type,
23 | // 'msg': msg,
24 | // 'close': function() {
25 | // alertService.closeAlert(this);
26 | // }
27 | // });
28 | // };
29 |
30 | // alertService.closeAlert = function(alert) {
31 | // alertService.closeAlertIdx($rootScope.alerts.indexOf(alert));
32 | // };
33 |
34 | // alertService.closeAlertIdx = function(index) {
35 | // $rootScope.alerts.splice(index, 1);
36 | // };
37 |
38 | alertService.open = function(msg) {
39 | $uibModal.open({
40 | template: '' + msg + "
",
41 | size: 'sm'
42 | });
43 | };
44 | return alertService;
45 | });
--------------------------------------------------------------------------------
/ng_app/views/assetCreate.html:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ng_app/views/assetDetail.html:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
24 |
31 |
32 |
33 |
34 |
35 | 资产名称
36 |
37 |
38 | {{asset.name}}
39 |
40 |
41 |
42 |
43 |
44 | 资产类型
45 |
46 |
47 | {{asset.type}}
48 |
49 |
50 |
51 |
52 |
53 | 资产发行总数量
54 |
55 |
56 | {{asset.amount}}
57 |
58 |
59 |
60 |
61 |
62 | 资产余额
63 |
64 |
65 | {{asset.balance}}
66 |
67 |
68 |
69 |
70 |
71 | 资产地址
72 |
73 |
74 | {{asset.asset_addr}}
75 |
76 |
77 |
78 |
79 |
80 | 发行方账户地址
81 |
82 |
83 | {{asset.account_addr}}
84 |
85 |
86 |
87 |
88 |
89 | 资产单位
90 |
91 |
92 | {{asset.unit}}
93 |
94 |
95 |
96 |
97 |
98 | 资产描述
99 |
100 |
101 | {{asset.description}}
102 |
103 |
104 |
105 |
106 |
107 | 资产创建时间
108 |
109 |
110 | {{asset.create_time}}
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/ng_app/views/assetIssue.html:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ng_app/views/assetList.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 | 创建资产
7 |
8 | -
9 | 发行资产
10 |
11 | -
12 | 资产列表
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
资产类型
28 |
29 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 资产名称 |
41 | 资产数量 |
42 | 资产类型 |
43 | 创建时间 |
44 |
45 |
46 |
47 |
48 | {{item.name}} |
49 | {{item.balance}} |
50 | {{item.type}} |
51 | {{item.create_time}} |
52 |
53 |
54 |
55 |
56 |
每页{{perPage}}条,共{{numPages}}页
57 |
58 |
--------------------------------------------------------------------------------
/ng_app/views/changePwd.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/ng_app/views/dashboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 浙大区块链实验室
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
98 |
99 |
100 |
105 |
106 |
109 |
110 |
111 |
112 |
113 |
115 |
134 |
136 |
137 |
141 |
142 |
--------------------------------------------------------------------------------
/ng_app/views/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/ng_app/views/register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/ng_app/views/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 | 资产
6 |
7 | -
8 | 创建资产
9 |
10 | -
11 | 发行资产
12 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
This is a test page!
26 |
--------------------------------------------------------------------------------
/ng_app/views/transactionDetail.html:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
28 | 资产名称
29 |
30 |
31 | {{trx.asset_name}}
32 |
33 |
34 |
35 |
36 |
37 | 交易hash
38 |
39 |
40 | {{trx.hash}}
41 |
42 |
43 |
44 |
45 |
46 | {{trx.type}}数量
47 |
48 |
49 | {{trx.amount}}
50 |
51 |
52 |
53 |
54 |
55 | 交易时间
56 |
57 |
58 | {{trx.create_time}}
59 |
60 |
61 |
62 |
63 |
64 | 对方帐号
65 |
66 |
67 | {{trx.other_party.phone}}
68 |
69 |
70 |
71 |
72 |
73 | 对方地址
74 |
75 |
76 | {{trx.other_party.account_addr}}
77 |
78 |
79 |
80 |
81 |
82 | 发行方帐号
83 |
84 |
85 | {{trx.issuer.phone}}
86 |
87 |
88 |
89 |
90 |
91 | 发行方账户地址
92 |
93 |
94 | {{trx.issuer.account_addr}}
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/ng_app/views/transactionList.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 | 转出资产
6 |
7 | -
8 | 交易记录
9 |
10 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
交易类型
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 | 资产名称 |
40 | 交易数量 |
41 | 交易类型 |
42 | 对方帐号 |
43 | 交易时间 |
44 |
45 |
46 |
47 |
48 | {{item.asset_name}} |
49 | {{item.amount}} |
50 | {{item.type}} |
51 | {{item.other_party.phone}} |
52 | {{item.create_time}} |
53 |
54 |
55 |
56 |
每页{{perPage}}条,共{{numPages}}页
57 |
58 |
--------------------------------------------------------------------------------
/ng_app/views/transfer.html:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ng_app/views/transferConfirm.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | 资产名称
9 |
10 |
11 | {{transfer.name}}
12 |
13 |
14 |
15 |
16 |
17 | 对方帐号
18 |
19 |
20 | {{transfer.other-account}}
21 |
22 |
23 |
24 |
25 |
26 | 对方地址
27 |
28 |
29 | {{transfer.other-addr}}
30 |
31 |
32 |
33 |
34 |
35 | 转出资产数目
36 |
37 |
38 | {{transfer.count}}
39 |
40 |
41 |
42 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/ng_app/views/user.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 | 帐号
22 |
23 |
24 | {{user.account}}
25 |
26 |
27 |
28 |
29 | 地址
30 |
31 |
32 | {{user.address}}
33 |
34 |
35 |
36 |
37 | 帐号创建时间
38 |
39 |
40 | {{user.time}}
41 |
42 |
43 |
44 |
49 |
60 |
61 |
62 | 密码
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/ng_app/views/verifyPwd.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "postinstall": "bower install",
7 | "prestart": "npm install",
8 | "start": "node ./bin/www"
9 | },
10 | "dependencies": {
11 | "body-parser": "~1.13.2",
12 | "bower": "^1.8.0",
13 | "cookie-parser": "~1.3.5",
14 | "crypto": "0.0.3",
15 | "debug": "~2.2.0",
16 | "ethereumjs-util": "^5.1.2",
17 | "express": "~4.13.1",
18 | "express-jwt": "^3.4.0",
19 | "hpc-web3": "^0.1.29-alpha",
20 | "jade": "~1.11.0",
21 | "jwt-simple": "^0.5.0",
22 | "mongodb": ">= 1.3.19",
23 | "mongoose": ">= 3.8.0",
24 | "morgan": "~1.6.1",
25 | "passport": "^0.3.2",
26 | "passport-local": "^1.0.0",
27 | "request": "^2.72.0",
28 | "secp256k1": "^3.3.0",
29 | "serve-favicon": "~2.3.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/routes/Users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by tyrion on 16-5-26.
3 | */
4 | var express = require('express');
5 | var Users = require('../models/Users').Users;
6 | var authToken = require('../models/Users').authToken;
7 | var jwt = require('jwt-simple');
8 | var request = require('request');
9 | var router = express.Router();
10 | var crypto = require('crypto');
11 | var ethereumUtil = require('ethereumjs-util');
12 | var secp256k1 = require('secp256k1');
13 | var GETH_URL = require('../help_utils/geth_level_utils').BASE_URL;
14 | var bcconf = require('../blockchain_lib/conf')
15 |
16 | var options = {
17 | method: 'POST',
18 | url: 'https://api.hyperchain.cn/v1/token/gtoken',
19 | formData:
20 | {
21 | client_id: bcconf.client_id,
22 | client_secret: bcconf.client_secret,
23 | phone: bcconf.phone,
24 | password: bcconf.password
25 | }
26 | };
27 |
28 | router.post('/user', function (req, res) {
29 | console.log(typeof req.body);
30 | var phone = req.body.phone;
31 | var password = req.body.password;
32 | console.log(req.body.phone);
33 | console.log(req.body.password);
34 | if(!(req.body.phone && req.body.password)){
35 | return res.status(400).send({
36 | "status": "failed",
37 | "msg": "账号和密码不为空"
38 | });
39 | }
40 | Users.findOne({"phone":phone},function(err, user){
41 | //若未查询到,user为null,避免在回调中使用user.something
42 | console.log(err);
43 | console.log("find user:"+user);
44 | if(err){
45 | return res.status(500).send({
46 | "status": "failed",
47 | "msg": "Something wrong with Server"
48 | });
49 | }
50 | if(user){
51 | return res.status(403).send({
52 | "status": "failed",
53 | "msg": "用户:"+phone+" 已被注册."
54 | });
55 | }
56 | var new_user = new Users({
57 | "phone": phone,
58 | "password": password,
59 | "create_time": new Date
60 | });
61 |
62 | request(options, function (error, response, body) {
63 | if (error) throw new Error(error);
64 |
65 | console.log(body);
66 | console.log(body['token_type'])
67 | var obj = JSON.parse(body)
68 | var accessToken = obj['access_token'];
69 | // newKey = getNewKey()
70 | request({
71 | url: "https://api.hyperchain.cn" + "/v1/dev/account/create" ,
72 | method: "GET",
73 | json: true,
74 | headers: {
75 | "Accept": "Accept: text/html",
76 | "Authorization": accessToken
77 | }
78 | },
79 | function (error, response, body) {
80 | if (!error && response.statusCode == 200) {
81 | var privatekey;
82 | do {
83 | privatekey = crypto.randomBytes(32);
84 | } while (!secp256k1.privateKeyVerify(privatekey));
85 | var publicKey = ethereumUtil.privateToPublic(privatekey).toString('hex');
86 | var address = "0x" + ethereumUtil.privateToAddress(privatekey).toString('hex');
87 | console.log(body)
88 | console.log(body.address)
89 | address = body.address;
90 | // return {
91 | // "address": address,
92 | // "privateKey" : privatekey.toString('hex'),
93 | // "publicKey" : publicKey
94 | // }
95 | nextStep(address,privatekey)
96 | } else {
97 | console.log(response.statusCode);
98 | }
99 | });
100 | });
101 | function nextStep(address,privatekey) {
102 | new_user.account_addr = address;
103 | new_user.private_key = privatekey;
104 | new_user.save(function(err){
105 | if (!err){
106 | console.log("4");
107 | var token = jwt.encode({
108 | id: new_user.id,
109 | phone: new_user.phone,
110 | password: new_user.password,
111 | exp: Date.now()+1000*60*60*24*365*10 //10year
112 | }, "jwtTokenSecret");
113 | var json_user=new_user.toJson();
114 | json_user.token = token;
115 | return res.send({
116 | "status":"ok",
117 | "msg":"创建成功",
118 | "data": json_user
119 | });
120 | } else {
121 | return res.status(403).send({
122 | "status":"failed",
123 | "msg":err
124 | });
125 | }
126 |
127 | });
128 | }
129 | });
130 | });
131 |
132 |
133 | router.get('/user',authToken, function(req, res){
134 | console.log("req.body:"+req.body);
135 | console.log("req.header:"+req.headers);
136 | console.log("req.query:"+req.query);
137 | res.send({
138 | data: req.user.toJson(),
139 | status: "ok"
140 | })
141 | });
142 | module.exports = router;
143 |
--------------------------------------------------------------------------------
/routes/asset.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Vampire on 16-5-27.
3 | */
4 | var express = require('express');
5 | var Assets = require('../models/asset').Assets;
6 | var Transaction = require("../models/transaction").Transaction;
7 | var ASSET_TYPE = require("../models/asset").ASSET_TYPE;
8 | var verify_psw = require("../models/Users").verify_psw;
9 | var TRANSACTION_TYPE = require("../models/transaction").TRANSACTION_TYPE;
10 | var GETH_URL = require("../help_utils/geth_level_utils").BASE_URL;
11 | var blockchain_lib = require("../blockchain_lib/lib");
12 | var checkForContractAddress = blockchain_lib.ForContractAddress;
13 | var newAsset = blockchain_lib.newAsset;
14 | var getTimestamp = blockchain_lib.getTimestamp;
15 | var random_16bits = blockchain_lib.random_16bits;
16 | var checkForContractTransaction = blockchain_lib.checkForContractTransaction;
17 | var issueAsset = blockchain_lib.issueAsset;
18 |
19 | var router = express.Router();
20 | var request = require('request');
21 |
22 | router.post('/', verify_psw, function(req, res) {
23 | console.log(JSON.stringify(req.user))
24 | var name = req.body.name;
25 | var unit = req.body.unit;
26 | var description = req.body.description;
27 |
28 | //check the information
29 | if(!(name
30 | // && amount && logo
31 | && unit && description)){
32 | return res.status(400).send({
33 | 'status': 'failed',
34 | 'msg': 'please fill all fields!'
35 | });
36 |
37 | //add fields check codes here
38 |
39 | }
40 | // request.post(GETH_URL+"/assets",{form:{address:req.user.account_addr}},function(error,response,body){
41 | newAsset(req.user.account_addr, req.user.private_key, function(error, address){
42 | if(!error && address){
43 | var new_asset = new Assets({
44 | name: name,
45 | unit: unit,
46 | description: description,
47 | logo: "http://fanyi.baidu.com/static/translation/img/header/logo_cbfea26.png",
48 | account_addr: req.user.account_addr,
49 | create_time: new Date,
50 | type: ASSET_TYPE.ISSUE
51 | });
52 | new_asset.asset_addr = address;
53 | new_asset.save();
54 | return res.send({
55 | 'status': 'ok',
56 | 'msg': 'create new asset ok',
57 | 'data': new_asset.toJson()
58 | });
59 | }else{
60 | return res.status(412).send({
61 | 'status': 'failed',
62 | 'msg': '创建资产失败,请重试',
63 | 'data': {}
64 | })
65 | }
66 | });
67 | });
68 |
69 | /*
70 | var _Asset = new Schema({
71 | name : { type: String, required: true
72 | // , unique: true TODO:资产名可以不唯一
73 | },
74 | amount : { type: Number, required: true, default: 0},
75 | unit : { type: String, required: true},
76 | description : { type: String, required: true},
77 | logo : { type: String, required: true},
78 | account_addr : { type: String, required: true},
79 | create_time : { type: Date, default: Date.now },
80 | asset_addr: {type: String, required: true},
81 | type: {type:String, required:true, default:ASSET_TYPE.ISSUE}
82 | });
83 | */
84 | router.get('/', function(req, res){
85 | var type = req.query.type;
86 | var asset_addr = req.query.asset_addr;//查询某一特定资产
87 | if (asset_addr){
88 | return Assets.findOne({asset_addr: asset_addr, account_addr: req.user.account_addr}, function (err, asset) {
89 | if(err){
90 | return res.status(400).send({
91 | status: "failed",
92 | msg: "something wrong with query asset"
93 | })
94 | }
95 | if (asset){
96 | return res.send({
97 | status: "ok",
98 | data: asset.toJson()
99 | })
100 | }
101 | })
102 | }
103 |
104 | var filter = {'account_addr': req.user.account_addr};
105 | switch(type){
106 | case "issue": filter['type'] = ASSET_TYPE.ISSUE; break;
107 | case "receive": filter['type'] = ASSET_TYPE.RECEIVING;break;
108 | default:break;
109 | }
110 |
111 | return Assets.find(filter, function(err, assets){
112 | if(assets[0]==null){
113 | return res.send({
114 | data: [],
115 | status: 'success'
116 | });
117 | }else{
118 | var asset_list = [];
119 | for (var i =0; i< assets.length; i++){
120 | asset_list[i] = assets[i].toJson();
121 | }
122 | return res.send({
123 | data: asset_list,
124 | status: 'ok'
125 | });
126 | }
127 | });
128 | });
129 |
130 | //发布资产
131 | router.put('/', verify_psw, function(req, res){
132 | var asset_addr = req.body.asset_addr;
133 | var amount = req.body.amount || 0;
134 | if(!(asset_addr && amount)){
135 | return res.status(400).send({
136 | 'status': 'failed',
137 | 'msg': '请求数据错误'
138 | });
139 | }
140 | Assets.find({'asset_addr': asset_addr, account_addr:req.user.account_addr}, null, {sort: [{'_id': -1}]}, function (err, assets) {
141 | if(assets[0]==null){
142 | return res.status(404).send({
143 | msg: '资产不存在',
144 | status: 'failed'
145 | });
146 | }else if(assets[0].type!=ASSET_TYPE.ISSUE){
147 | return res.status(412).send({
148 | status:"failed",
149 | msg:"没有发行权"
150 | })
151 | }else{
152 | issueAsset(req.user.account_addr, req.user.private_key, asset_addr, amount, function(error, receipt){
153 | if(!error && receipt.txHash){
154 | var new_trsac = new Transaction({
155 | from: req.user.account_addr,
156 | to: req.user.account_addr,
157 | amount: amount,
158 | create_time: new Date,
159 | asset_addr: asset_addr,
160 | type: TRANSACTION_TYPE.ISSUE,
161 | hash: receipt.txHash
162 | });
163 | new_trsac.save(function(err){
164 | console.log(err);
165 | });
166 | assets[0].amount += amount;
167 | assets[0].balance += amount;
168 | assets[0].save(function(err){
169 | console.log(err);
170 | });
171 | return res.send({
172 | status: 'ok',
173 | msg: "发布成功",
174 | data: assets[0].toJson()
175 | });
176 | }else{
177 | return res.send({
178 | 'status': 'failed',
179 | 'msg': '发布资产失败,请重试',
180 | 'data': {}
181 | })
182 | }
183 | })
184 | }
185 | })
186 | });
187 |
188 | module.exports = router;
189 |
--------------------------------------------------------------------------------
/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | router.get('/', function (req, res) {
4 | res.sendfile("./ng_app/index.html");
5 | });
6 |
7 | router.get('/login', function (req, res) {
8 | res.sendfile("./ng_app/views/login.html");
9 | });
10 |
11 |
12 | router.get('/register', function (req, res) {
13 | res.sendfile("./ng_app/views/register.html");
14 | });
15 |
16 | router.get('/dashboard', function (req, res) {
17 | res.sendfile("./ng_app/views/dashboard.html");
18 | });
19 |
20 | module.exports.router = router;
21 |
--------------------------------------------------------------------------------
/routes/password.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Tyrion on 2016/6/8.
3 | */
4 | var express = require('express');
5 | var Users = require('../models/Users').Users;
6 | var authToken = require('../models/Users').authToken;
7 | var veryfy_psw = require('../models/Users').verify_psw;
8 | var jwt = require('jwt-simple');
9 | var router = express.Router();
10 |
11 | router.put('/',authToken,veryfy_psw,function(req,res){
12 | var new_psw = req.body.new_psw;
13 | if(!new_psw){
14 | return res.status(400).send({
15 | status:"failed",
16 | msg:"请输入新密码"
17 | })
18 | }else{
19 | req.user.password = new_psw;
20 | var token = jwt.encode({
21 | id: req.user.id,
22 | phone: req.user.phone,
23 | password: req.user.password,
24 | exp: Date.now()+1000*60*60*24*365*10 //10year
25 | }, "jwtTokenSecret");
26 | req.user.save();
27 | return res.send({
28 | status: "ok",
29 | msg: "密码修改成功",
30 | token:token
31 | })
32 | }
33 | });
34 |
35 | module.exports = router;
--------------------------------------------------------------------------------
/routes/token.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Tyrion on 2016/5/25.
3 | */
4 | var express = require('express');
5 | var router = express.Router();
6 | var Users = require('../models/Users').Users;
7 | var jwt = require('jwt-simple');
8 | var app = require('../app');
9 |
10 | router.post('/token', function (req, res) {
11 | //TODO validate req.body.username and req.body.password
12 | //if is invalid, return 401
13 | console.log(req.body.phone);
14 | console.log(req.body.password);
15 | Users.findOne({"phone":req.body.phone},function(err, user){
16 | if(!user){
17 | return res.status(404).send({
18 | "status": "failed",
19 | "msg": "no such user"
20 | })
21 | }
22 | user.comparePassword(req.body.password,function(is_match){
23 | if(is_match){
24 | console.log('enter if');
25 | console.log("ready to run jwt.sign");
26 | var token = jwt.encode({
27 | id: user.id,
28 | phone: user.phone,
29 | password: user.password,
30 | exp: Date.now()+1000*60*60*24*365*10 //10year
31 | }, "jwtTokenSecret");
32 | res.send({ token: token });
33 | }else{
34 | console.log('enter else');
35 | res.status(401).send({
36 | "status": "failed",
37 | "msg": "Wrong password,authenticate failed"
38 | })
39 | }
40 | })
41 | });
42 | });
43 |
44 | module.exports = router;
--------------------------------------------------------------------------------
/routes/transaction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Vampire on 16-6-1.
3 | * Update by Tyrion on 16/6/2
4 | */
5 | var express = require('express');
6 | var Transaction = require('../models/transaction').Transaction;
7 | var User = require('../models/Users').Users;
8 | var verify_psw = require('../models/Users').verify_psw;
9 | var Asset = require("../models/asset").Assets;
10 | var ASSET_TYPE = require("../models/asset").ASSET_TYPE;
11 | var TRANSACTION_TYPE = require('../models/transaction').TRANSACTION_TYPE;
12 | var request = require('request');
13 | var GETH_URL = require('../help_utils/geth_level_utils').BASE_URL;
14 | var blockchain_lib = require("../blockchain_lib/lib");
15 | var checkForContractAddress = blockchain_lib.ForContractAddress;
16 | var newAsset = blockchain_lib.newAsset;
17 | var getTimestamp = blockchain_lib.getTimestamp;
18 | var random_16bits = blockchain_lib.random_16bits;
19 | var checkForContractTransaction = blockchain_lib.checkForContractTransaction;
20 | var newTransaction = blockchain_lib.newTransaction;
21 | var issueAsset = blockchain_lib.issueAsset;
22 |
23 | var router = new express.Router();
24 |
25 | router.get('/',function(req, res){
26 | console.log(req.query);
27 | var type = req.query.type;
28 | var filter = {};
29 | console.log("user_id:"+req.user.id);
30 | switch(type){
31 | case "issue": filter = {
32 | type:TRANSACTION_TYPE.ISSUE,
33 | from:req.user.account_addr
34 | }; break;
35 | case "receive": filter = {
36 | type:TRANSACTION_TYPE.TRANSACTION,
37 | to:req.user.account_addr
38 | }; break;
39 | case "transfer": filter = {
40 | type:TRANSACTION_TYPE.TRANSACTION,
41 | from:req.user.account_addr
42 | }; break;
43 | default:filter = {"$or":[{from:req.user.account_addr},{to:req.user.account_addr}]};
44 | }
45 | console.log(filter);
46 | Transaction.find(filter, null, {sort: [{'_id': -1}]},function(err, transactions){
47 | if(err) {
48 | return res.status(500).send({
49 | "status": "failed",
50 | "msg": "Something wrong with Server"
51 | });
52 | }
53 | console.log('123');
54 | if(transactions[0]){
55 | var tr_list=[];
56 | //console.log(transactions);
57 | for (var i=0; i