├── views ├── qrtest.html ├── index.ejs ├── signup.ejs ├── profile.ejs ├── login.ejs ├── withdraw.ejs └── deposit.ejs ├── config ├── database.js ├── bchconfig.js ├── ethereum.js └── passport.js ├── .gitattributes ├── README.md ├── package.json ├── scripts ├── create_bchhdprivatekey.js └── create_database.js ├── server.js ├── .gitignore ├── service ├── keystorebch.js ├── keystoreeth.js ├── db_service.js └── bitcoincashrpc.js └── app ├── routes.js ├── eth_transactions.js └── bch_transactions.js /views/qrtest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | // config/database.js 2 | module.exports = { 3 | 'connection': { 4 | 'host': 'localhost', 5 | 'user': 'root', 6 | 'password': '@x6iyLxk_2vH' 7 | }, 8 | 'database': 'my_schema', 9 | 'users_table': 'users', 10 | 'ethereum_transaction':'eth_transaction', 11 | 'bch_transaction':'bch_transaction', 12 | "eth_withdraw_log":'eth_withdraw_log', 13 | "bch_withdraw_log":'bch_withdraw_log' 14 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /config/bchconfig.js: -------------------------------------------------------------------------------- 1 | // configurations for BCH deposit service 2 | var bitcorecash = require('bitcoincashjs'); 3 | Networks = bitcorecash.Networks; 4 | Networks.enableRegtest(); 5 | 6 | module.exports = { 7 | 'network': Networks.testnet, 8 | 'host': 'localhost', 9 | 'username': 'admin', 10 | 'password':'password', 11 | 'port': 8332, 12 | 'minStartBlock': 0, // When scan the block, the starting point 13 | 'minConfirmation':20, 14 | "coinsPerBCH":1000, 15 | 'withdrawSourceHDPath': ['m/0\'/1\'/0\''],//The address associate with this HDpath is mvpVLHH3UmACHJ1JV6tcneZFB5pqpvVs9j. 16 | 'minTxFee':0.001 17 | }; -------------------------------------------------------------------------------- /config/ethereum.js: -------------------------------------------------------------------------------- 1 | // configurations for Ethereum deposit service 2 | module.exports = { 3 | 'host': 'http://localhost:8545', //Ip address and port of the ethereum node service 4 | 'minMinedRequirement' : 20, //a transaction will be deposited only after N mined blocks. 5 | 'minStartBlock': 44984, // When scan the block, the starting point 6 | 'WeisPerCoin': 1000000000000000, 7 | 'ethDepositCheckInterval': 10000, // In milliseconds 8 | 'keyStorePassword':'0.8487677677962937', 9 | 'withdrawSourceAccount': '0xafdf17bcb4b9a99ab2fdf30b6f0d62b59ea42bdd' 10 | //withdrawsouce in Jeremy's regtest network is: mvpVLHH3UmACHJ1JV6tcneZFB5pqpvVs9j 11 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Complete Guide to Coin and Cryptocurrecny Exchange Demo 2 | 3 | This is an onging project. 4 | 5 | Current version database is ported to MySQL 6 | 7 | We will be using Passport to authenticate users locally, 8 | 9 | ## Instructions 10 | 11 | If you would like to download the code and try it for yourself: 12 | 13 | 1. Clone the repo: `git clone git@github.com:JeremywangCN/coin-cryptocurrency-exchange.git` 14 | 1. Install packages: `npm install` 15 | 1. Edit the database configuration: `config/database.js` 16 | 1. Edit the ethereum configuration: 'config/ethereum.js' 17 | 1. Create the database schema: `node scripts/create_database.js` 18 | 1. Create a BCH HDkey for demo: `node scripts/create_bchhdprivatekey.js` 19 | 1. Launch: `node server.js` 20 | 1. Visit in your browser at: `http://localhost:8080` 21 | 22 | Licence: 1 23 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Coins and Cryptocurrency Exchange 6 | 7 | 8 | 11 | 12 | 13 |
14 |
15 |

Coins and Cryptocurrency Exchange

16 | 17 |

Login or Register with:

18 | 19 | Local Login 20 | Local Signup 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-authentication", 3 | "main": "server.js", 4 | "scripts": { 5 | "start": "node ./server" 6 | }, 7 | "dependencies": { 8 | "axios": "^0.18.0", 9 | "bchaddrjs": "^0.2.1", 10 | "bcrypt-nodejs": "0.0.3", 11 | "npm-address-translator": "^1.0.4", 12 | "qr-image": "^3.2.0", 13 | "bitcoincashjs": "^0.1.10", 14 | "bitcore-lib": "^0.15.0", 15 | "body-parser": "^1.13.1", 16 | "connect-flash": "^0.1.1", 17 | "cookie-parser": "^1.3.5", 18 | "ejs": "^2.3.2", 19 | "eth-lightwallet": "^3.0.1", 20 | "express": "^4.13.0", 21 | "express-session": "^1.11.3", 22 | "hooked-web3-provider": "^1.0.0", 23 | "morgan": "^1.6.0", 24 | "mysql": "^2.7.0", 25 | "passport": "^0.2.2", 26 | "passport-local": "^1.0.0", 27 | "qrcode": "^1.2.0", 28 | "web3": "^0.16.0" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/JeremywangCN/coin-cryptocurrency-exchange.git" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /scripts/create_bchhdprivatekey.js: -------------------------------------------------------------------------------- 1 | // This script creates a BIP 32 HD Private Key and save as a file to the server. 2 | 3 | var bitcorecash = require('bitcoincashjs'); 4 | var HDPrivateKey = bitcorecash.HDPrivateKey; 5 | 6 | var hdPrivateKey = new HDPrivateKey(); 7 | var hdPublicKey = hdPrivateKey.hdPublicKey; 8 | var publickeyString = hdPublicKey.toString(); 9 | var privatekeyString = hdPrivateKey.toString(); 10 | 11 | const fs = require('fs'); 12 | fs.writeFile('./keystorebchprivate.txt', privatekeyString, function(err){ 13 | // throws an error, you could also catch it here 14 | if (err) throw err; 15 | 16 | // success case, the file was saved 17 | console.log('BCH Private key '+ hdPrivateKey.toString()+'was saved to keystorebch.txt!'); 18 | 19 | }); 20 | fs.writeFile('./keystorebchpublic.txt', publickeyString, function(err){ 21 | // throws an error, you could also catch it here 22 | if (err) throw err; 23 | 24 | // success case, the file was saved 25 | console.log('BCH Public key '+ publickeyString+'was saved to keystorebch.txt!'); 26 | 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /views/signup.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node Authentication 6 | 7 | 8 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |

Signup

18 | 19 | 20 | <% if (message.length > 0) { %> 21 |
<%= message %>
22 | <% } %> 23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 |
37 | 38 |
39 | 40 |

Already have an account? Login

41 |

Or go home.

42 | 43 |
44 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /views/profile.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Coins and Cryptocurrency Exchange 6 | 7 | 8 | 9 | 12 | 13 | 14 |
15 | 16 | 20 | 21 |
22 |
23 |
24 |

<%= user.username %>

25 | 26 |

27 | ID: <%= user.id %>
28 | Coin: <%= user.coin %>
29 | Ethereum (GWei): <%= user.eth_value %>
30 | Bicoin Cash(Satoshi): <%= user.bch_value %> 31 |

32 | 33 |
34 | 35 |
36 | 37 |
38 | 41 | 44 | 47 | 48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node Authentication 6 | 7 | 8 | 11 | 12 | 13 |
14 |
15 |

Login

16 | 17 | 18 | <% if (message.length > 0) { %> 19 |
<%= message %>
20 | <% } %> 21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 | 37 | 38 |
39 | 40 |
41 | 42 |

Need an account? Signup

43 |

Or go home.

44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // set up ====================================================================== 4 | // get all the tools we need 5 | var express = require('express'); 6 | var session = require('express-session'); 7 | var cookieParser = require('cookie-parser'); 8 | var bodyParser = require('body-parser'); 9 | var morgan = require('morgan'); 10 | var app = express(); 11 | var port = process.env.PORT || 8080; 12 | var passport = require('passport'); 13 | var flash = require('connect-flash'); 14 | var ethConfig = require('./config/ethereum'); 15 | 16 | app.use(express.static('public')); 17 | // Initiate Ethereum Keystore and Eth Web3 instance. Start to check node for new unrecorded transactions. 18 | var keystore = require('./service/keystoreeth'); 19 | var Eth = require('./app/eth_transactions'); 20 | keystore.init(function (ifSucceed,ksi) { 21 | if(ifSucceed){ 22 | Eth.init(); 23 | } 24 | }); 25 | 26 | // Init BCH HD Public key and wallet accounts 27 | var BCH = require('./app/bch_transactions'); 28 | var bchKeyUtils= require('./service/keystorebch'); 29 | bchKeyUtils.init(function (err) { 30 | if(err); 31 | else 32 | BCH.updateUnspentTx(); 33 | }); 34 | 35 | BCH.initRecipientAddresses(); 36 | 37 | setInterval(function () { 38 | Eth.checkBlockChainForMinedTxService(); 39 | BCH.checkBlockChainForDepositTx(); 40 | BCH.updateSubmittedWithdrawTxs(); 41 | },ethConfig.ethDepositCheckInterval); 42 | 43 | // configuration =============================================================== 44 | // connect to our database 45 | 46 | require('./config/passport')(passport); // pass passport for configuration 47 | 48 | 49 | // set up our express application 50 | app.use(morgan('dev')); // log every request to the console 51 | app.use(cookieParser()); // read cookies (needed for auth) 52 | app.use(bodyParser.urlencoded({ 53 | extended: true 54 | })); 55 | app.use(bodyParser.json()); 56 | 57 | app.set('view engine', 'ejs'); // set up ejs for templating 58 | 59 | // required for passport 60 | app.use(session({ 61 | secret: 'vidyapathaisalwaysrunning', 62 | resave: true, 63 | saveUninitialized: true 64 | } )); // session secret 65 | app.use(passport.initialize()); 66 | app.use(passport.session()); // persistent login sessions 67 | app.use(flash()); // use connect-flash for flash messages stored in session 68 | 69 | 70 | 71 | // routes ====================================================================== 72 | require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport 73 | 74 | // launch ====================================================================== 75 | app.listen(port); 76 | console.log('The magic happens on port ' + port); -------------------------------------------------------------------------------- /scripts/create_database.js: -------------------------------------------------------------------------------- 1 | 2 | var mysql = require('mysql'); 3 | var dbconfig = require('../config/database'); 4 | 5 | var connection = mysql.createConnection(dbconfig.connection); 6 | 7 | connection.query('CREATE DATABASE ' + dbconfig.database); 8 | 9 | connection.query('\ 10 | CREATE TABLE `' + dbconfig.database + '`.`' + dbconfig.users_table + '` ( \ 11 | `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, \ 12 | `username` VARCHAR(20) NOT NULL, \ 13 | `coin` INT DEFAULT 0, \ 14 | `eth_address` VARCHAR(50),\ 15 | `bch_address` VARCHAR(50),\ 16 | `password` CHAR(60) NOT NULL, \ 17 | PRIMARY KEY (`id`), \ 18 | UNIQUE INDEX `id_UNIQUE` (`id` ASC), \ 19 | UNIQUE INDEX `username_UNIQUE` (`username` ASC) \ 20 | )'); 21 | 22 | 23 | connection.query('\ 24 | CREATE TABLE `' + dbconfig.database + '`.`' + dbconfig.ethereum_transaction + '` ( \ 25 | `txhash` VARCHAR(100), \ 26 | `to_address` VARCHAR(50) NOT NULL, \ 27 | `to_user_id` VARCHAR(50) NOT NULL, \ 28 | `value` BIGINT DEFAULT 0, \ 29 | `blocknumber` INT,\ 30 | `timestamp` TIMESTAMP, \ 31 | PRIMARY KEY (`txhash`), \ 32 | INDEX `to_user_id` (`to_user_id` ASC) \ 33 | )'); 34 | 35 | 36 | connection.query("CREATE TABLE `" + dbconfig.database + '`.`' + dbconfig.eth_withdraw_log + "` ( \ 37 | `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\ 38 | `tx_status` INT DEFAULT 0 ,\ 39 | `tx_hash` VARCHAR(100), \ 40 | `from_address` VARCHAR(50) NOT NULL, \ 41 | `to_address` VARCHAR(50) NOT NULL, \ 42 | `to_user_id` VARCHAR(50) NOT NULL, \ 43 | `eth_value` BIGINT DEFAULT 0, \ 44 | `coin_value` INT DEFAULT 0, \ 45 | `block_number` INT,\ 46 | `timestamp` TIMESTAMP, \ 47 | PRIMARY KEY (`id`))"); 48 | 49 | 50 | connection.query('\ 51 | CREATE TABLE `' + dbconfig.database + '`.`' + dbconfig.bch_transaction + '` ( \ 52 | `txhash` VARCHAR(100), \ 53 | `to_address` VARCHAR(50) NOT NULL, \ 54 | `to_user_id` VARCHAR(50) NOT NULL, \ 55 | `value` BIGINT DEFAULT 0, \ 56 | `blocknumber` INT,\ 57 | `timestamp` TIMESTAMP, \ 58 | PRIMARY KEY (`txhash`), \ 59 | INDEX `to_user_id` (`to_user_id` ASC) \ 60 | )'); 61 | 62 | 63 | connection.query("CREATE TABLE `" + dbconfig.database + '`.`' + dbconfig.bch_withdraw_log + "` ( \ 64 | `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\ 65 | `tx_status` INT DEFAULT 0 ,\ 66 | `tx_hash` VARCHAR(100), \ 67 | `from_address` VARCHAR(50) NOT NULL, \ 68 | `to_address` VARCHAR(50) NOT NULL, \ 69 | `to_user_id` VARCHAR(50) NOT NULL, \ 70 | `bch_value` BIGINT DEFAULT 0, \ 71 | `coin_value` INT DEFAULT 0, \ 72 | `block_number` INT,\ 73 | `timestamp` TIMESTAMP, \ 74 | PRIMARY KEY (`id`))"); 75 | 76 | console.log('Success: Database Created!'); 77 | 78 | connection.end(); 79 | -------------------------------------------------------------------------------- /views/withdraw.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Coins and Cryptocurrency Exchange 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 | 18 | 24 | 25 |
26 | 27 |
28 |
29 |

30 | ID: <%= user.id %>
31 | Username: <%= user.username %>
32 | Coin: <%= user.coin %>
33 | Ethereum: <%= user.eth_amount %>
34 | Bitcoin Cash: <%= user.bch_amount %>
35 | 36 |

37 |

38 |

39 | To Ethereum Address: 40 | 41 | Withdraw coin amount: 42 | 43 | 44 |
45 | 46 | 47 |

48 |

49 |

50 | To Bitcoin Cash Address: 51 | 52 | Withdraw coin amount: 53 | 54 | 55 |
56 | 57 |

58 | 59 |
60 |
61 | 62 |
63 | 64 |
65 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | 217 | ###### 218 | # Idea 219 | ###### 220 | .idea/ 221 | 222 | ############## 223 | # node modules 224 | ############## 225 | node_modules/ 226 | -------------------------------------------------------------------------------- /service/keystorebch.js: -------------------------------------------------------------------------------- 1 | var BCHConfig = require('./../config/bchconfig')//Bitcoin Cash Configurations 2 | 3 | const fs = require('fs');//File system 4 | 5 | var DBService = require('./../service/db_service');//Mysql Database Services 6 | 7 | // RPC connection to the bitcoin cash node 8 | var BCHRpc = require("./../service/bitcoincashrpc"); 9 | var BCHRpcConnect = new BCHRpc(BCHConfig.host, BCHConfig.username, BCHConfig.password, BCHConfig.port, 3000); 10 | 11 | //Nodejs bitcoin cash modules 12 | var BCHCashCore = require("bitcoincashjs"); 13 | var HDPublicKey = BCHCashCore.HDPublicKey; 14 | var HDPrivateKey = BCHCashCore.HDPrivateKey; 15 | var Address = BCHCashCore.Address; 16 | 17 | 18 | var hdPublicKey; 19 | var hdPrivateKey; 20 | 21 | // withdrawSource array includes each withdraw source account's private key and address. The address is Base58check format. 22 | var withdrawSource=[]; 23 | 24 | function initBCHPublicKey(callback) { 25 | fs.readFile('keystorebchpublic.txt', 'utf8', function(err, data) { 26 | if (err) { 27 | console.log(err); 28 | 29 | } 30 | else{ 31 | hdPublicKey = new HDPublicKey(data); 32 | return callback; 33 | } 34 | } 35 | 36 | ); 37 | 38 | }; 39 | 40 | function initBCHPrivateKey(callback) { 41 | fs.readFile('keystorebchprivate.txt', 'utf8', function(err, data) { 42 | if (err) { 43 | return callback(err); 44 | 45 | } 46 | else{ 47 | hdPrivateKey = new HDPrivateKey(data); 48 | return callback(null,hdPrivateKey); 49 | } 50 | } 51 | 52 | ); 53 | 54 | }; 55 | 56 | 57 | //Add addresses in withdrawSource array into bitcoin cash node with account "withdraw source". Make it possisble to check unspent transactions from nodejs. 58 | function addWithdrawSourceAddrToWallet(callback) { 59 | 60 | if(withdrawSource.length===0) 61 | setWithdrawSource(); 62 | var BCC = require("./../service/bitcoincashrpc"); 63 | var BCHRrcClient = new BCC(BCHConfig.host, BCHConfig.username, BCHConfig.password, BCHConfig.port, 3000);//Bitcoin Cash RPC Connection 64 | var p; 65 | for(var i=0;i{ 69 | return callback(null, i); 70 | }); 71 | }; 72 | 73 | 74 | //set up withdrawSource Array 75 | function setWithdrawSource() { 76 | var hdPath = BCHConfig.withdrawSourceHDPath; 77 | for (var i=0; i< hdPath.length; i++){ 78 | var derivedPrivateKey = hdPrivateKey.derive(hdPath[i]); 79 | var derivedAddressBase58Check = new Address(derivedPrivateKey.publicKey, BCHConfig.network,'pubkeyhash'); 80 | withdrawSource.push({derivedPrivateKey:derivedPrivateKey,address: derivedAddressBase58Check.toString()}); 81 | } 82 | }; 83 | 84 | function updateUserToAddress(username) { 85 | 86 | var derivedByArgument = hdPublicKey.derive("m/0/0"); 87 | DBService.getUserByUsername(username,function (err, users) { 88 | if(err) 89 | { 90 | console.log(err); 91 | return; 92 | }else{ 93 | var userid = users[0].id; 94 | var derivedAddressBase58Check = new Address(derivedByArgument.derive(userid).publicKey, BCHConfig.network,'pubkeyhash'); 95 | var BASE58CHECK = BCHCashCore.encoding.Base58Check; 96 | var derivedAddressHex ="0x"+BASE58CHECK.decode(derivedAddressBase58Check.toString()).toString('hex').slice(2); 97 | 98 | var updateList ={bch_address:derivedAddressHex}; 99 | DBService.updateUsersById(userid,updateList,function (err2,data2) { 100 | if(err2){ 101 | console.log(err); 102 | return; 103 | } 104 | else{ 105 | console.log("The new username "+ username+", BCH Hex address is "+ derivedAddressHex+". BCH Base58check address is "+derivedAddressBase58Check.toString()); 106 | var p = Promise.resolve(BCHRpcConnect.importaddress(derivedAddressBase58Check.toString(),username,false)); 107 | p.then(info=>{ 108 | console.log("Import the address to the node wallet!") 109 | }); 110 | } 111 | }); 112 | } 113 | }); 114 | }; 115 | 116 | module.exports ={ 117 | init:function(callback){ 118 | initBCHPublicKey(); 119 | initBCHPrivateKey(function (err, privateKey) { 120 | if(err) 121 | return (err); 122 | else { 123 | setWithdrawSource(); 124 | addWithdrawSourceAddrToWallet(function (err, amount) { 125 | if (err) 126 | return callback(err); 127 | else{ 128 | return callback(null); 129 | } 130 | }); 131 | } 132 | 133 | }); 134 | }, 135 | updateUserToAddress:updateUserToAddress, 136 | withdrawSource:withdrawSource, 137 | hdPrivateKey:hdPrivateKey, 138 | setWithdrawSource:setWithdrawSource, 139 | } 140 | -------------------------------------------------------------------------------- /service/keystoreeth.js: -------------------------------------------------------------------------------- 1 | var dbconfig = require('./../config/database'); 2 | var ethconfig = require('./../config/ethereum'); 3 | const DBSERVICE = require('./../service/db_service'); 4 | const fs = require('fs'); 5 | 6 | var ks ; 7 | 8 | function setksintance(ksi) { 9 | ks = ksi; 10 | console.log('The key store instance is good...'+ks); 11 | }; 12 | 13 | function getKeyStore() { 14 | return ks; 15 | }; 16 | 17 | function initKeystore(callback) { 18 | const fs = require('fs'); 19 | ks = fs.readFile('keystore.txt', 'utf8', function(err, data) { 20 | if (err) { 21 | console.log(err); 22 | var lightwallet = require('eth-lightwallet'); 23 | var seed = lightwallet.keystore.generateRandomSeed(); 24 | var password = Math.random().toString(); 25 | console.log('The seed is '+seed); 26 | console.log('The password is '+password); 27 | 28 | lightwallet.keystore.createVault({ 29 | password: password, 30 | seedPhrase: seed, 31 | hdPathString: "m/0'/0'/0'" 32 | }, function (err, ks) { 33 | 34 | if(err){ 35 | console.log(err); 36 | return callback(false); 37 | }else{ 38 | 39 | ks.keyFromPassword(password, function (err, pwDerivedKey) { 40 | if(err) 41 | { 42 | console.log('Error keystore instance from password!'); 43 | return callback(false); 44 | } 45 | else 46 | { 47 | var serialized = ks.serialize(); 48 | 49 | fs.writeFile('keystore.txt', serialized, function(err){ 50 | // throws an error, you could also catch it here 51 | if (err) throw err; 52 | 53 | // success case, the file was saved 54 | console.log('Keystore saved!'); 55 | return callback(true,ks); 56 | 57 | 58 | }); 59 | 60 | } 61 | 62 | }); 63 | 64 | } 65 | 66 | }); 67 | } 68 | else{ 69 | var deserialize = data; 70 | var lightwallet = require('eth-lightwallet'); 71 | var keystoreinstance = lightwallet.keystore.deserialize(deserialize); 72 | console.log("Load keystore from local file."); 73 | 74 | keystoreinstance.keyFromPassword(ethconfig.keyStorePassword, function (err, pwDerivedKey) { 75 | if(err) 76 | { 77 | console.log(err); 78 | }else{ 79 | keystoreinstance.passwordProvider = function (callback) { 80 | callback(null, ethconfig.keyStorePassword); 81 | }; 82 | } 83 | }); 84 | return callback(true,keystoreinstance); 85 | 86 | } 87 | } 88 | 89 | ); 90 | 91 | }; 92 | 93 | function updateUserToAddress(username) { 94 | 95 | ks.keyFromPassword(ethconfig.keyStorePassword, function (err, pwDerivedKey) { 96 | if(err) 97 | { 98 | return console(err); 99 | } 100 | else 101 | { 102 | DBSERVICE.getUserByUsername(username,function (err,data) { 103 | if(err) { 104 | console.log(err); 105 | return; 106 | } 107 | if(ks.getAddresses().length 2 | 3 | 4 | 5 | 6 | Coins and Cryptocurrency Exchange 7 | 8 | 9 | 10 | 13 | 14 | 15 |
16 | 17 | 26 | 27 |
28 | 29 | 30 | 31 | 32 |
33 |
34 |
Ethereum
35 |

Address: <%= user.eth_address %>

36 |

Balance (GWeis): <%= user.eth_amount %>

37 |

38 | 39 |
40 | 41 |

42 |
43 |
44 | 45 |
46 |
47 |
48 |
Bitcoin Cash
49 |

Address: <%= user.bch_address %>

50 |

Balance (Satoshi): <%= user.bch_amount %>

51 |

52 | 53 |
54 | 55 |

56 |
57 |
58 | 59 |
60 |
61 | 62 | 63 | 192 |
193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | // app/routes.js 2 | module.exports = function(app, passport) { 3 | app.use(function(req, res, next){ 4 | 5 | next(); 6 | }); 7 | 8 | // ===================================== 9 | // HOME PAGE (with login links) ======== 10 | // ===================================== 11 | app.get('/', function(req, res) { 12 | res.render('index.ejs'); // load the index.ejs file 13 | }); 14 | 15 | // ===================================== 16 | // LOGIN =============================== 17 | // ===================================== 18 | // show the login form 19 | app.get('/login', function(req, res) { 20 | 21 | // render the page and pass in any flash data if it exists 22 | res.render('login.ejs', { message: req.flash('loginMessage') }); 23 | }); 24 | 25 | // process the login form 26 | app.post('/login', passport.authenticate('local-login', { 27 | successRedirect : '/profile', // redirect to the secure profile section 28 | failureRedirect : '/login', // redirect back to the signup page if there is an error 29 | failureFlash : true // allow flash messages 30 | }), 31 | function(req, res) { 32 | console.log("hello"); 33 | 34 | if (req.body.remember) { 35 | req.session.cookie.maxAge = 1000 * 60 * 3; 36 | } else { 37 | req.session.cookie.expires = false; 38 | } 39 | res.redirect('/'); 40 | }); 41 | 42 | // ===================================== 43 | // SIGNUP ============================== 44 | // ===================================== 45 | // show the signup form 46 | app.get('/signup', function(req, res) { 47 | // render the page and pass in any flash data if it exists 48 | res.render('signup.ejs', { message: req.flash('signupMessage') }); 49 | }); 50 | 51 | // process the signup form 52 | app.post('/signup', passport.authenticate('local-signup', { 53 | successRedirect : '/profile', // redirect to the secure profile section 54 | failureRedirect : '/signup', // redirect back to the signup page if there is an error 55 | failureFlash : true // allow flash messages 56 | })); 57 | 58 | // ===================================== 59 | // PROFILE SECTION ========================= 60 | // ===================================== 61 | // we will want this protected so you have to be logged in to visit 62 | // we will use route middleware to verify this (the isLoggedIn function) 63 | app.get('/profile', isLoggedIn, function(req, res) { 64 | console.log(req.session.id) 65 | let user = req.user; 66 | let DBService = require('./../service/db_service'); 67 | let ETHTransaction = require('./eth_transactions'); 68 | let BCHTransaction = require('./bch_transactions'); 69 | 70 | DBService.getUserByUsername(user.username,function (err,data) { 71 | if(err) { 72 | console.log(err); 73 | user.eth_value = -1; 74 | } 75 | else 76 | ETHTransaction.getETHBalanceByUserId(data[0].id,function (err,ethData) { 77 | if(!err){ 78 | user.eth_value = ethData; 79 | BCHTransaction.getBCHBalanceByUserId(this.userId,function (err,bchData) { 80 | if(!err){ 81 | user.bch_value = bchData; 82 | } 83 | res.render('profile.ejs', {user : user}); 84 | }) 85 | } 86 | else 87 | res.render('profile.ejs', { 88 | user : user // get the user out of session and pass to template 89 | }); 90 | }.bind({userId:data[0].id})) 91 | }) 92 | }); 93 | 94 | // ===================================== 95 | // LOGOUT ============================== 96 | // ===================================== 97 | app.get('/logout', function(req, res) { 98 | req.logout(); 99 | res.redirect('/'); 100 | }); 101 | 102 | 103 | //Leo 绘制eth转账二维码界面 104 | app.get('/deposit', isLoggedIn,function(req, res) { 105 | // render the page and pass in any flash data if it exist 106 | var QRCode = require('qrcode'); 107 | const DBService =require('./../service/db_service'); 108 | let ETHTransaction = require('./eth_transactions'); 109 | let BCHTransaction = require('./bch_transactions'); 110 | DBService.getUserByUsername(req.user.username,function (err, rows) { 111 | if (err){ 112 | console.log(err); 113 | // return done(false); 114 | } 115 | if (rows.length==0){ 116 | console.log("Cannot find this user name in DB!"); 117 | // return done(false); 118 | } 119 | QRCode.toDataURL(rows[0].eth_address, function (err, url) { 120 | let eth_url = url; 121 | QRCode.toDataURL(this.row.bch_address,function (err1,url1) { 122 | let bch_url = url1; 123 | let user = this.req.user; 124 | user.eth_address = this.row.eth_address; 125 | user.bch_address = this.row.bch_address; 126 | ETHTransaction.getETHBalanceByUserId(this.row.id,function (err,ethData) { 127 | user.eth_amount=ethData; 128 | BCHTransaction.getBCHBalanceByUserId(this.row.id,function (err,bchData) { 129 | user.bch_amount=bchData; 130 | this.res.render('deposit.ejs', { user: user, imgUrlEth: eth_url,imgUrlBCH:bch_url}); 131 | }.bind({row:this.row,res:res,req:req,user:user})) 132 | }.bind({row:this.row,res:res,req:req,user:user})); 133 | }.bind({row: rows[0],res:res,req:req})) 134 | }.bind({row: rows[0],res:res,req:req})) 135 | }); 136 | }); 137 | 138 | //Leo eth转出接口 139 | app.post('/ethwithdraw',isLoggedIn,function(req, res){ 140 | let Eth = require('./eth_transactions'); 141 | Eth.withdrawETH(req.user.username,req.body.ethAddress,req.body.ethAmount,function (err,data) { 142 | if(err) console.log(err); 143 | else{ 144 | // res.render('withdraw.ejs', { user: req.user,msg: "Coin was withdrawn as Ethereum. The transaction ID is "+data}); 145 | res.redirect('/withdraw?eth_tx='+data,); 146 | 147 | } 148 | }); 149 | 150 | }); 151 | app.post('/bchwithdraw',isLoggedIn,function(req, res){ 152 | console.log(req.user); 153 | let Bch = require('./bch_transactions'); 154 | Bch.withdrawBCH(req.user.username,req.body.bchAddress,req.body.bchAmount,function (err,data) { 155 | if(err) console.log(err); 156 | else{ 157 | // res.render('withdraw.ejs', { user: req.user,msg: "Coin was withdrawn as Bitcoin Cash. The transaction ID is "+data}); 158 | res.redirect('/withdraw?bch_tx='+data,); 159 | } 160 | }); 161 | 162 | }); 163 | app.get('/withdraw',isLoggedIn,function(req, res){ 164 | const DBService =require('./../service/db_service'); 165 | let ETHTransaction = require('./eth_transactions'); 166 | let BCHTransaction = require('./bch_transactions'); 167 | console.log(req.body); 168 | ETHTransaction.getETHBalanceByUserId(req.user.id,function (err,ethData) { 169 | this.req.user.eth_amount=ethData; 170 | BCHTransaction.getBCHBalanceByUserId(this.req.user.id,function (err,bchData) { 171 | this.req.user.bch_amount=bchData; 172 | res.render('withdraw.ejs', { user: req.user}); 173 | }.bind({res:res,req:req})) 174 | }.bind({res:res,req:req})); 175 | 176 | }); 177 | 178 | app.get('/ajaxdeposit',isLoggedIn,function (req,res) { 179 | const ETHTX= require('./eth_transactions'); 180 | ETHTX.getUnconfirmedDepositTxByUsername(req.user.username,function (err,data) { 181 | if(err) console.log(err); 182 | res.send(data); 183 | }) 184 | }); 185 | 186 | app.get('/ajaxbchdeposit',isLoggedIn,function (req,res) { 187 | const BCHTX= require('./bch_transactions'); 188 | BCHTX.getUnconfirmedDepositTxByUsername(req.user.username,function (err,data) { 189 | if(err) console.log(err); 190 | res.send(data); 191 | }) 192 | }); 193 | 194 | app.get('/ajaxethbalance',isLoggedIn,function (req,res) { 195 | const ETHTX= require('./eth_transactions'); 196 | ETHTX.getETHBalanceByUsername(req.user.username,function (err,data) { 197 | if(err) console.log(err); 198 | res.send({'ethBalance':data}); 199 | }) 200 | }); 201 | 202 | app.get('/ajaxbchbalance',isLoggedIn,function (req,res) { 203 | const BCHTX= require('./bch_transactions'); 204 | BCHTX.getBCHBalanceByUserId(req.user.id,function (err,data) { 205 | if(err) console.log(err); 206 | res.send({'bchBalance':data}); 207 | }) 208 | }); 209 | 210 | app.get('/ajaxcoinbalance',isLoggedIn,function (req,res) { 211 | const DBService = require('./../service/db_service'); 212 | DBService.getUserByUsername(req.user.username,function (err,data) { 213 | if(err) console.log(err); 214 | res.send({'coinBalance':data[0].coin}); 215 | }) 216 | }); 217 | 218 | app.get('/ajaxethtx',isLoggedIn,function (req,res) { 219 | const ETHTX= require('./eth_transactions'); 220 | let returnTx=ETHTX.getETHTransactionByTxHash(req.query.eth_tx); 221 | res.send(returnTx); 222 | }); 223 | 224 | app.get('/ajaxbchtx',isLoggedIn,function (req,res) { 225 | const BCHTX= require('./bch_transactions'); 226 | BCHTX.getBCHTransactionByTxHash(req.query.bch_tx,function (err,data) { 227 | res.send(data); 228 | }); 229 | }); 230 | }; 231 | 232 | // route middleware to make sure 233 | function isLoggedIn(req, res, next) { 234 | 235 | // if user is authenticated in the session, carry on 236 | if (req.isAuthenticated()) 237 | return next(); 238 | 239 | // if they aren't redirect them to the home page 240 | res.redirect('/'); 241 | } 242 | -------------------------------------------------------------------------------- /app/eth_transactions.js: -------------------------------------------------------------------------------- 1 | //Import modules 2 | const Web3 = require("web3"); 3 | const KS = require('./../service/keystoreeth'); 4 | const HookedWeb3Provider = require("hooked-web3-provider"); 5 | const ETHCONFIG = require('./../config/ethereum'); 6 | const DBSERVICE = require('./../service/db_service'); 7 | 8 | 9 | // System variables 10 | var web3; 11 | var latestCheckedBlock; 12 | var recipientAddresses; 13 | var unconfirmedDepositTxs; 14 | 15 | const withdrawLogStatus ={ 16 | CREATED: 0, 17 | SUBMITTED: 2, 18 | MINED: 4 19 | }; 20 | 21 | function initWeb3() { 22 | let ks = KS.getKeyStore(); 23 | let provider = new HookedWeb3Provider({ 24 | host: ETHCONFIG.host, 25 | transaction_signer: ks 26 | }); 27 | web3 = new Web3(provider); 28 | console.log("The web3 instance was initiated successfully. It is "+web3); 29 | } 30 | 31 | function initLatestCheckedBlock() { 32 | DBSERVICE.getLatestETHCheckedBlock(function (err,data) { 33 | if(err) latestCheckedBlock = ETHCONFIG.minStartBlock; 34 | else { 35 | latestCheckedBlock= Math.max(data,ETHCONFIG.minStartBlock); 36 | } 37 | console.log("The latest checked block was initiated successfully. It is "+latestCheckedBlock); 38 | }) 39 | } 40 | 41 | 42 | function initRecipientAddresses() { 43 | DBSERVICE.getAllUsers(function (err,data) { 44 | if(!err){ 45 | let addressArray = []; 46 | for(let i=0;i=latestMinedBlock-ETHCONFIG.minMinedRequirement){ 68 | console.log('The lasted checked block is '+ latestMinedBlock+'. No new mined blocks........'); 69 | } 70 | else 71 | checkDepositTxs(recipientAddresses,latestCheckedBlock+1,latestMinedBlock-ETHCONFIG.minMinedRequirement); 72 | } 73 | checkSubmittedWithdrawTxs(); 74 | unconfirmedDepositTxs =getUnconfirmedDepositTx(recipientAddresses); 75 | } 76 | 77 | function checkDepositTxs(myaccounts, startBlockNumber, endBlockNumber) { 78 | if(typeof myaccounts ==='undefined') return; 79 | 80 | if((startBlockNumber = 0) { 94 | DBSERVICE.getUserByEthAddress(transaction.to,function (err,users) { 95 | if(err) { 96 | console.log(err); 97 | return; 98 | } 99 | let depositCoinNumber = this.transaction.value/ETHCONFIG.WeisPerCoin; 100 | let currentCoinNumber = users[0].coin; 101 | let username = users[0].username; 102 | DBSERVICE.updateCoinByUsername(depositCoinNumber+currentCoinNumber,username,function (err,data) { 103 | if(err) { 104 | console.log(err); 105 | return; 106 | } 107 | DBSERVICE.addETHDepositTransaction(this.transaction.hash,this.transaction.to,this.user.id,this.transaction.value/1000000000,this.transaction.blockNumber,function (err,data) { 108 | if(err) { 109 | console.log(err); 110 | return; 111 | } 112 | console.log(" Find a new ETH deposit transaction. TX is "+transaction.hash+". The recipient user id is "+this.user.id); 113 | }.bind({transaction:transaction,user:this.user})); 114 | 115 | }.bind({transaction:transaction,user:users[0]})); 116 | 117 | }.bind({transaction:transaction})); 118 | } 119 | }); 120 | if(i>latestCheckedBlock) 121 | latestCheckedBlock=i; 122 | } 123 | } 124 | } 125 | 126 | 127 | function getUnconfirmedDepositTx(addressArray) { 128 | 129 | let latestMinedBlock = web3.eth.getBlock('latest').number; 130 | let startBlock = latestMinedBlock- ETHCONFIG.minMinedRequirement; 131 | let unconfirmedDepositTxs = []; 132 | for(let i= startBlock;i<=latestMinedBlock;i++){ 133 | let block = web3.eth.getBlock(i, true); 134 | block.transactions.forEach(function (tx) { 135 | if(addressArray.indexOf(tx.to)>=0) 136 | { 137 | let returnTx ={'blockHash':tx.blockHash,'eth_address':tx.to,'confirmations':latestMinedBlock-tx.blockNumber,'need':20} 138 | unconfirmedDepositTxs.push(returnTx) 139 | } 140 | }); 141 | } 142 | return unconfirmedDepositTxs; 143 | } 144 | 145 | function getUnconfirmedDepositTxByUsername(username,callback) { 146 | DBSERVICE.getUserByUsername(username,function (err,data) { 147 | if(err) return callback(err); 148 | let latestMinedBlock = web3.eth.getBlock('latest').number; 149 | let startBlock = latestMinedBlock- ETHCONFIG.minMinedRequirement; 150 | let eth_address= data[0].eth_address; 151 | let unconfirmedTx = []; 152 | for(let i= 0;i1){ 17 | return done("More than one users with this username "+username+" were found!"); 18 | } 19 | return done(null, rows); 20 | }); 21 | } 22 | 23 | function getUserByUseId(userId,done) { 24 | connection.query("SELECT * FROM "+DBCONFIG.database+ "."+ DBCONFIG.users_table +" where id='"+userId+"'", function(err, rows){ 25 | if (err){ 26 | return done(err); 27 | } 28 | else if(rows.length==0){ 29 | return done("No users with this user id "+userId+" were found!"); 30 | } 31 | else if(rows.length>1){ 32 | return done("More than one users with this id "+userId+" were found!"); 33 | } 34 | return done(null, rows); 35 | }); 36 | } 37 | 38 | function getUserByEthAddress(eth_address,done) { 39 | connection.query("SELECT * FROM "+DBCONFIG.database+ "."+ DBCONFIG.users_table +" where eth_address='"+eth_address+"'", function(err, rows){ 40 | if (err){ 41 | return done(err); 42 | } 43 | else if(rows.length==0){ 44 | return done("No users with this eth_address "+eth_address+" were found!"); 45 | } 46 | else if(rows.length>1){ 47 | return done("More than one users with this eth_address "+eth_address+" were found!"); 48 | } 49 | return done(null, rows); 50 | }); 51 | } 52 | 53 | 54 | function getAllUsers(done) { 55 | connection.query("SELECT * FROM "+DBCONFIG.database+ ".users", function(err, rows){ 56 | if (err){ 57 | return done(err); 58 | } 59 | if (rows.length===0){ 60 | return done("No Users are found in Mysql database!"); 61 | } 62 | return done(null, rows); 63 | }); 64 | } 65 | 66 | function updateCoinByUsername(coin,username,done) { 67 | 68 | connection.query("UPDATE "+DBCONFIG.database+".users SET coin = ? WHERE username = ?",[coin, username], function(err, rows){ 69 | if (err){ 70 | return done(err); 71 | } 72 | return done(null,rows); 73 | }); 74 | } 75 | 76 | function addBCHDepositTransaction(txhash,to_address,to_user_id,value,blocknumber,done) { 77 | 78 | let insertQuery = "INSERT INTO "+DBCONFIG.database+".bch_transaction ( txhash, to_address, to_user_id, value, blocknumber )" + 79 | " values ('"+txhash+"','"+to_address+"',"+to_user_id+","+value+","+blocknumber+")"; 80 | 81 | connection.query(insertQuery, function (err, rows) { 82 | if(err) 83 | return done(err); 84 | else{ 85 | return done(null, rows); 86 | } 87 | }); 88 | } 89 | 90 | 91 | function addETHDepositTransaction(txhash,to_address,to_user_id,value,blocknumber,done) { 92 | 93 | let insertQuery = "INSERT INTO "+DBCONFIG.database+".eth_transaction ( txhash, to_address, to_user_id, value, blocknumber )" + 94 | " values ('"+txhash+"','"+to_address+"',"+to_user_id+","+value+","+blocknumber+")"; 95 | connection.query(insertQuery, function (err, rows) { 96 | if(err){ 97 | return done(err); 98 | } 99 | else{ 100 | return done(null, rows); 101 | } 102 | }); 103 | } 104 | 105 | 106 | function getBCHDepositTransactionByTxId(txid, done) { 107 | connection.query("SELECT * FROM "+DBCONFIG.database+ "."+DBCONFIG.bch_transaction+ " where txhash = '" +txid+"'", function(err, rows){ 108 | if (err){ 109 | return done(err); 110 | } 111 | return done(null, rows); 112 | }); 113 | } 114 | 115 | function addBCHWithdrawLog(userid, toAddress, coinAmount,status,done) { 116 | 117 | let from_address = BCHCONFIG.withdrawSourceAccount; 118 | let to_address = toAddress; 119 | let to_userId = userid; 120 | let coin_value= coinAmount; 121 | let bch_value = 100000000*coinAmount /BCHCONFIG.coinsPerBCH; 122 | let tx_status = status; 123 | 124 | let insertQuery = "INSERT INTO "+DBCONFIG.database+"."+ DBCONFIG.bch_withdraw_log+"( from_address, to_address, to_user_id, coin_value, bch_value, tx_status)" + 125 | " values ('"+from_address+"','"+to_address+"',"+to_userId+","+coin_value+","+bch_value+","+tx_status+")"; 126 | 127 | connection.query(insertQuery,function (err, result) { 128 | if(err){ 129 | return done(err); 130 | }else{ 131 | let withdrawLog = { 132 | id:result.insertId, 133 | from_address: from_address, 134 | to_address:to_address, 135 | to_userId: to_userId, 136 | coin_value: coin_value, 137 | bch_value: bch_value, 138 | tx_status: tx_status 139 | }; 140 | return done(null,withdrawLog ); 141 | } 142 | }) 143 | 144 | } 145 | 146 | 147 | 148 | function addETHWithdrawLog(userid, toAddress, coinAmount,status,done) { 149 | 150 | let from_address = ETHCONFIG.withdrawSourceAccount; 151 | let to_address = toAddress; 152 | let to_userId = userid; 153 | let coin_value = coinAmount; 154 | let eth_value = coinAmount*ETHCONFIG.WeisPerCoin/1000000000; 155 | let tx_status = status; 156 | 157 | let insertQuery = "INSERT INTO "+DBCONFIG.database+"."+ DBCONFIG.eth_withdraw_log+"( from_address, to_address, to_user_id, coin_value, eth_value, tx_status)" + 158 | " values ('"+from_address+"','"+to_address+"',"+to_userId+","+coin_value+","+eth_value+","+tx_status+")"; 159 | 160 | connection.query(insertQuery,function (err, result) { 161 | if(err){ 162 | return done(err); 163 | }else{ 164 | let withdrawLog = { 165 | id:result.insertId, 166 | from_address: from_address, 167 | to_address:to_address, 168 | to_userId: to_userId, 169 | coin_value: coin_value, 170 | eth_value: eth_value, 171 | tx_status: tx_status 172 | }; 173 | return done(null,withdrawLog ); 174 | } 175 | }) 176 | 177 | } 178 | 179 | function updateBCHWithdrawLogById(id,list,done) { 180 | let keys = Object.keys(list); 181 | let values = Object.values(list); 182 | let setString = 'set '; 183 | for(let i=0; i { 328 | return response.data.result; 329 | }) 330 | .catch(err => { 331 | console.log('failed in setaccount', err); 332 | return err.message; 333 | }); 334 | } 335 | 336 | 337 | async importaddress(...params) { 338 | /*if (!this.isValidAddress(...params)) { 339 | console.log('failed valid check'); 340 | return 'invalid address given'; 341 | }*/ 342 | 343 | let req = await this.performMethod('importaddress', ...params); 344 | 345 | return axios(req) 346 | .then(response => { 347 | return response.data.result; 348 | }) 349 | .catch(err => { 350 | console.log('failed in importaddress', err); 351 | return err.message; 352 | }); 353 | } 354 | 355 | /** 356 | * @param {String} account bitcoind account to send from 357 | * @param {String} address bitcoin address to send to 358 | * @param {Number} Amount number of bitcoin to send 359 | * @return {String} Tx returns the transaction ID 360 | */ 361 | async sendFrom(...params) { 362 | if (!this.isValidAddress(params[1])) { 363 | console.log('failed valid check'); 364 | return 'invalid address given'; 365 | } 366 | 367 | let req = await this.performMethod('sendFrom', ...params); 368 | 369 | return axios(req) 370 | .then(response => { 371 | return response.data.result; 372 | }) 373 | .catch(err => { 374 | console.log('failed in sendFrom', err); 375 | return err.message; 376 | }); 377 | } 378 | 379 | /** 380 | * @param {String} accountName name of account you want the address for 381 | * @return {String} address returns the address 382 | */ 383 | async getAccountAddress(...params) { 384 | let req = await this.performMethod('getAccountAddress', ...params); 385 | 386 | return axios(req) 387 | .then(response => { 388 | return response.data.result; 389 | }) 390 | .catch(err => { 391 | console.log('failed in getAccountAddress', err); 392 | return err.message; 393 | }); 394 | } 395 | /** 396 | * @param {String} blockhash 397 | * @return {obj} data returns the block info 398 | */ 399 | async getBlock(...params) { 400 | if (!this.isValidAddress(...params)) { 401 | console.log('failed valid check'); 402 | return 'invalid address given'; 403 | } 404 | 405 | let req = await this.performMethod('getBlock', ...params); 406 | 407 | return axios(req) 408 | .then(response => { 409 | return response.data.result; 410 | }) 411 | .catch(err => { 412 | console.log('failed in getBlock', err); 413 | return err.message; 414 | }); 415 | } 416 | 417 | /** 418 | * @param {String} transaction_id 419 | * @param {Number} vout use 1 420 | * @return {obj} data returns the tx info 421 | */ 422 | async getTxOut(...params) { 423 | if (!this.isValidAddress(...params)) { 424 | console.log('failed valid check'); 425 | return 'invalid address given'; 426 | } 427 | 428 | let req = await this.performMethod('getTxOut', ...params); 429 | 430 | return axios(req) 431 | .then(response => { 432 | return response.data.result; 433 | }) 434 | .catch(err => { 435 | console.log('failed in getTxOut', err); 436 | return err.message; 437 | }); 438 | } 439 | 440 | isValidAddress(...x) { 441 | console.log("Valid address "+x); 442 | const test = '[13CH][a-km-zA-HJ-NP-Z0-9]{30,33}'; 443 | const cashRegEx = /^((?:bitcoincash):)?(?:[023456789acdefghjklmnpqrstuvwxyz]){42}$/gi; 444 | let testRegEx = new RegExp(test, 'i'); 445 | 446 | if (testRegEx.test(x)) { 447 | return testRegEx.test(x); 448 | } else { 449 | return cashRegEx.test(x); 450 | } 451 | } 452 | 453 | translateAddress(address) { 454 | let test = '[13CH][a-km-zA-HJ-NP-Z0-9]{30,33}'; 455 | let testRegEx = new RegExp(test, 'i'); 456 | if (testRegEx.test(address)) { 457 | let translated = translate.translateAddress(address); 458 | if (translated.origCoin == 'BTC') { 459 | return translated.origAddress; 460 | } else { 461 | return translated.resultAddress; 462 | } 463 | } 464 | } 465 | } 466 | 467 | module.exports = BitcoinCashRPC; 468 | -------------------------------------------------------------------------------- /app/bch_transactions.js: -------------------------------------------------------------------------------- 1 | //Import modules 2 | const MYSQL = require('mysql'); 3 | const BCC = require("./../service/bitcoincashrpc"); 4 | const BCHCONFIG = require('./../config/bchconfig'); 5 | const BCHADDR = require('bchaddrjs'); 6 | const DBSERVICE = require('./../service/db_service'); 7 | const BCHkeyStore = require('./../service/keystorebch'); 8 | 9 | 10 | // System variables 11 | const BCHRPCCLIENT = new BCC(BCHCONFIG.host, BCHCONFIG.username, BCHCONFIG.password, BCHCONFIG.port, 30000);//Bitcoin Cash RPC Connection 12 | 13 | //All the unspent transactions that belong to the withdraw source account. 14 | var unspentTxs =[]; 15 | 16 | const withdrawLogStatus ={ 17 | CREATED: 0, 18 | SUBMITTED: 2, 19 | MINED: 4 20 | }; 21 | 22 | function addRecipientAddresses(addressArray,callback) { 23 | const bitcorecash = require('bitcoincashjs'); 24 | const BASE58CHECK = bitcorecash.encoding.Base58Check; 25 | let count =0; 26 | for(let i=0;i=BCHCONFIG.minConfirmation)&& (this.transaction.category ==='receive')&&this.transaction.account===this.user.username) 82 | { 83 | let coinsDeposit = this.transaction.amount*BCHCONFIG.coinsPerBCH; 84 | let coinBalance = this.user.coin; 85 | var coinBalanceNew = coinBalance + coinsDeposit; 86 | console.log('coinBalanceNew is'+coinBalanceNew); 87 | console.log('username is'+this.user.username); 88 | 89 | DBSERVICE.updateCoinByUsername(coinBalanceNew,this.user.username,function (err3, rows3) { 90 | if(err3) 91 | console.log(err3); 92 | else{ 93 | let bch_value_satoshi =this.transaction.amount*100000000; 94 | 95 | DBSERVICE.addBCHDepositTransaction(this.transaction.txid,this.transaction. 96 | address,this.user.id,bch_value_satoshi,blockAcount-this.transaction.confirmations, 97 | function (err4, rows4) { 98 | if(err4) 99 | console.log(err4); 100 | else{ 101 | console.log("Found a new BCH deposit transaction and updated all the records accordingly!"); 102 | console.log("Txid is "+this.transaction.txid); 103 | } 104 | }.bind({transaction: this.transaction,user:this.user})); 105 | 106 | } 107 | }.bind({transaction: this.transaction,user:this.user})); 108 | } 109 | } 110 | else 111 | console.log("The transaction "+this.transaction.txid+" has already been handled by the system."); 112 | }.bind({transaction: transactions[j],user:this.user})); 113 | } 114 | }.bind({user:data[i]})); 115 | } 116 | } 117 | }); 118 | }); 119 | 120 | } 121 | }; 122 | 123 | /* 124 | This function will subtract a certain amount of Coin and send User BCH instead. 125 | username: who asks to withdraw BCH. 126 | toAddress: The address that the user input to receive BCH for this request. It is legacy format. 127 | coinAmount: Coin to subtract from the user's account 128 | For example: 129 | withdrawBCH('8','mhREfUGuqNTpQSuPVG234kt6T9hzVYkD8Y',5000000); 130 | */ 131 | function withdrawBCH(username, toAddress, coinAmount,callback) { 132 | DBSERVICE.getUserByUsername(username, function (err1, data1) { 133 | if (err1) { 134 | return callback(err1); 135 | } 136 | else { 137 | var newCoinValue = data1[0].coin - coinAmount; 138 | var user = data1[0]; 139 | user.coin = newCoinValue; 140 | if (newCoinValue < 0) { 141 | return callback("The user " + username + " does not have enough coins to withdraw!"); 142 | } 143 | DBSERVICE.updateCoinByUsername(newCoinValue, username, function (err2, data2) { 144 | if (err2) { 145 | return callback(err2); 146 | } else { 147 | DBSERVICE.addBCHWithdrawLog(this.user.id, toAddress, coinAmount, withdrawLogStatus.CREATED, function (err3, data3) { 148 | if (err3) 149 | return callback(err3); 150 | else { 151 | sendTxToNode(data3, function (err4, data4) { 152 | if (err4) 153 | return callback(err4); 154 | else { 155 | DBSERVICE.updateBCHWithdrawLogById(this.withdrawLog.id, { 156 | tx_hash: data4, 157 | tx_status: withdrawLogStatus.SUBMITTED 158 | }, function (err5, data5) { 159 | if (err5) 160 | return callback(err5); 161 | else { 162 | return callback(null,data4); 163 | } 164 | }); 165 | } 166 | }.bind({withdrawLog: data3})); 167 | } 168 | }); 169 | } 170 | }.bind({user: data1[0]})); 171 | } 172 | }); 173 | } 174 | function sendTxToNode(withdrawLog, done) { 175 | var bch = require('bitcoincashjs'); 176 | var bchkey = require('./../service/keystorebch'); 177 | updateUnspentTx(); 178 | var bch_value_bch = withdrawLog.coin_value / BCHCONFIG.coinsPerBCH; 179 | var bch_value_satoshi = 100000000*withdrawLog.coin_value / BCHCONFIG.coinsPerBCH; 180 | var withdrawUTXO =chooseUnspentTxForWithdraw(bch_value_bch); 181 | if(withdrawUTXO.err){ 182 | return done(withdrawUTXO.err); 183 | }else{ 184 | //The current rpc interface only accepts legacy address format, not Bitcoin cash format. We have to convert the address format before we spend these UTXOs. 185 | //bchaddrjs does not accept bchreg prefix, convert bchreg into regtest if it is necessary. 186 | for(var i=0; i{ 200 | return done(null,info); 201 | }); 202 | } 203 | }; 204 | 205 | function updateUnspentTx(){ 206 | 207 | var BCHKeyStore = require('./../service/keystorebch'); 208 | if(BCHKeyStore.withdrawSource.length===0) 209 | BCHKeyStore.setWithdrawSource(); 210 | var addresses =[]; 211 | for (var i=0; i{ 216 | unspentTxs = info; 217 | console.log("Retrieve unspent transaction list for withdraw source.") 218 | }); 219 | 220 | }; 221 | 222 | function chooseUnspentTxForWithdraw(withdrawAmount) { 223 | var withdrawFrom = []; 224 | for(var i=0; iwithdrawAmount+BCHCONFIG.minTxFee) { 235 | withdrawFrom = withdrawFrom.slice(0,j+1); 236 | return({err:null,UTXOs:withdrawFrom}); 237 | } 238 | } 239 | return ({err:"The account balance is insufficient."}); 240 | }; 241 | 242 | function updateSubmittedWithdrawTxs() { 243 | let promise = Promise.resolve(BCHRPCCLIENT.getBlockCount()); 244 | promise.then(function (blockAccount) { 245 | DBSERVICE.getBCHWithdrawLogByStatus(withdrawLogStatus.SUBMITTED,function (err,rows) { 246 | if(err) 247 | console.log(err); 248 | else{ 249 | for(var i=0;i=BCHCONFIG.minConfirmation) 254 | DBSERVICE.updateBCHWithdrawLogById(this.row.id,{tx_status:withdrawLogStatus.MINED,block_number:blockAccount-info.confirmations},function (err,date) { 255 | if(err) 256 | console.log(err); 257 | else{ 258 | console.log("Tx id "+this.row.tx_hash+" has been mined successfully!"); 259 | } 260 | }.bind({row:this.row})) 261 | }.bind({row:rows[i]})); 262 | 263 | } 264 | } 265 | }); 266 | }); 267 | } 268 | 269 | function getBCHBalanceByUserId(userId,done) { 270 | DBSERVICE.getBCHDepositeTxsByUserId(userId,function (err,data) { 271 | if(err) 272 | { 273 | return done(err); 274 | } 275 | let totalWithdraw =0; 276 | for(let i=0;i