├── .editorconfig ├── .eslintrc ├── randomScripts └── bcoin │ ├── getHDKey.js │ ├── getTx.js │ ├── create_new_wallet.js │ ├── walletInfo.js │ ├── sendBTC.js │ ├── paymentAddress.js │ └── spvNode.js ├── TODO.md ├── example └── index.js ├── package.json ├── LICENSE ├── .gitignore ├── README.md └── src └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{js,sql,md}] 4 | indent_style = space 5 | indent_size = 2 6 | max_line_length = 120 7 | quote_type = single 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base"], 3 | "rules": { 4 | "no-console": "off", 5 | "no-underscore-dangle": "off" 6 | }, 7 | "env": { 8 | "mocha": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /randomScripts/bcoin/getHDKey.js: -------------------------------------------------------------------------------- 1 | let id; 2 | id='primary' 3 | const {WalletClient} = require('bclient'); 4 | const {Network} = require('bcoin'); 5 | const network = Network.get('testnet'); 6 | 7 | const walletOptions = { 8 | network: network.type, 9 | port: network.walletPort, 10 | // apiKey: 'api-key' 11 | } 12 | 13 | const walletClient = new WalletClient(walletOptions); 14 | const wallet = walletClient.wallet(id); 15 | 16 | (async () => { 17 | const result = await wallet.getMaster(); 18 | console.log(result); 19 | })(); 20 | -------------------------------------------------------------------------------- /randomScripts/bcoin/getTx.js: -------------------------------------------------------------------------------- 1 | let id; let 2 | hash; 3 | id = 'primary'; 4 | hash = 'e2027a3e6d7a078c39b56b08ef1961c84a14370e828975ac4163b8bbeee48b3a'; 5 | const { WalletClient } = require('bclient'); 6 | const { Network } = require('bcoin'); 7 | 8 | const network = Network.get('testnet'); 9 | 10 | const walletOptions = { 11 | network: network.type, 12 | port: network.walletPort, 13 | // apiKey: 'api-key', 14 | }; 15 | 16 | const walletClient = new WalletClient(walletOptions); 17 | const wallet = walletClient.wallet(id); 18 | 19 | (async () => { 20 | const result = await wallet.getTX(hash); 21 | console.log(result); 22 | })(); 23 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - [x] config where to store the blockchain data and most importantly the wallet key (bcoin config `prefix`) [*ref*](https://github.com/bcoin-org/bcoin/blob/master/docs/Configuration.md) 2 | - [x] see syncing process([events](http://bcoin.io/guides/events.html)): `tail -F ~/.bcoin/debug.log | grep (net)` 3 | - [x] event when coin is accepted: `node.plugins.walletdb.wdb.on('confirmed', ()=>{})` 4 | - [x] user define blockchain location 5 | - [x] see chain syncing status 6 | - [x] get recovery phrase 7 | - [x] ~~pull out money~~ 8 | - [x] ~~get balance~~ 9 | - [x] prevent user from expose bcoin api publically(secret) 10 | - [x] doc 11 | - [x] landing page -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const Cryptoo = require('../src'); 2 | 3 | const cryptoo = new Cryptoo({ 4 | network: 'testnet', 5 | chainDataFolder: '~/.bcoin', 6 | secret: 'sample-secret-e34dc9dff1b8b04c2b678ff7bb1dd02181bfe31b045f77', 7 | }); 8 | 9 | cryptoo.createAddress() 10 | .then(address => console.log(`\nPayment address: ${address}`)); 11 | 12 | cryptoo.getRecoveryPhrase() 13 | .then(recoveryPhrase => console.log(`\nWallet recovery phrase: ${recoveryPhrase}`)); 14 | 15 | 16 | // send some bitcoins to the address to see the transctions after blockchain data is synced 17 | cryptoo.on('unconfirmedTx', (data) => { 18 | console.log(data, 'unconfirmedTx'); 19 | }); 20 | 21 | cryptoo.on('confirmedTx', (data) => { 22 | console.log(data, 'confirmedTx'); 23 | }); 24 | -------------------------------------------------------------------------------- /randomScripts/bcoin/create_new_wallet.js: -------------------------------------------------------------------------------- 1 | // Usage: support multiple applications? 2 | let id; let passphrase; let name; let 3 | type; 4 | id = 'primary'; 5 | passphrase = 'secret123'; 6 | name = 'menace'; 7 | type = 'multisig'; 8 | const { WalletClient } = require('bclient'); 9 | const { Network } = require('bcoin'); 10 | 11 | const network = Network.get('regtest'); 12 | 13 | const walletOptions = { 14 | network: network.type, 15 | port: network.walletPort, 16 | apiKey: 'api-key', 17 | }; 18 | 19 | const walletClient = new WalletClient(walletOptions); 20 | const wallet = walletClient.wallet(id); 21 | const options = { name, type, passphrase }(async () => { 22 | const result = await wallet.createAccount(name, options); 23 | console.log(result); 24 | })(); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cryptoo", 3 | "version": "1.0.2", 4 | "description": "pay with crypto currencies", 5 | "repository": "git://github.com/getcryptoo/cryptoo.git", 6 | "homepage": "https://github.com/getcryptoo/cryptoo", 7 | "bugs": { 8 | "url": "https://github.com/getcryptoo/cryptoo/issues" 9 | }, 10 | "main": "src/index.js", 11 | "scripts": { 12 | "test": "mocha -u qunit" 13 | }, 14 | "keywords": [ 15 | "pay", 16 | "bitcoin", 17 | "eth", 18 | "eos" 19 | ], 20 | "author": "timqian", 21 | "license": "MIT", 22 | "dependencies": { 23 | "bclient": "^0.1.4", 24 | "bcoin": "^1.0.2" 25 | }, 26 | "devDependencies": { 27 | "eslint": "^5.8.0", 28 | "eslint-config-airbnb-base": "^13.1.0", 29 | "eslint-plugin-import": "^2.14.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /randomScripts/bcoin/walletInfo.js: -------------------------------------------------------------------------------- 1 | const id = 'primary'; 2 | const { WalletClient } = require('bclient'); 3 | const { Network } = require('bcoin'); 4 | 5 | const network = Network.get('testnet'); 6 | 7 | const walletOptions = { 8 | network: network.type, 9 | port: network.walletPort, 10 | // apiKey: 'sample-api-key-e34dc9dff1b8b04c2b678ff7bb1dd02181bfe31b045f77', 11 | }; 12 | 13 | const walletClient = new WalletClient(walletOptions); 14 | const wallet = walletClient.wallet(id); 15 | 16 | (async () => { 17 | const result = await wallet.getInfo(); 18 | const accounts = await wallet.getAccount('default'); 19 | const allCoins = await wallet.getCoins(); 20 | console.log(result); 21 | console.log(accounts); 22 | console.log(allCoins); 23 | const master = await wallet.getMaster(); 24 | console.log(master); 25 | })(); -------------------------------------------------------------------------------- /randomScripts/bcoin/sendBTC.js: -------------------------------------------------------------------------------- 1 | // Usage: refound; swap out money 2 | let id; let passphrase; let rate; let value; let 3 | address; 4 | id = 'primary'; 5 | // passphrase = 'secret123'; 6 | rate = 1000; 7 | value = 10000; 8 | address = 'moR6DrHWZJUDrxTTsSDdC4KXrML7osrp7K'; 9 | 10 | const { WalletClient } = require('bclient'); 11 | const { Network } = require('bcoin'); 12 | 13 | const network = Network.get('testnet'); 14 | 15 | const walletOptions = { 16 | network: network.type, 17 | port: network.walletPort, 18 | // apiKey: 'api-key', 19 | }; 20 | 21 | const walletClient = new WalletClient(walletOptions); 22 | const wallet = walletClient.wallet(id); 23 | 24 | const options = { 25 | // passphrase, 26 | rate, 27 | outputs: [{ value, address }], 28 | }; 29 | 30 | (async () => { 31 | const result = await wallet.send(options); 32 | console.log(result); 33 | })(); 34 | -------------------------------------------------------------------------------- /randomScripts/bcoin/paymentAddress.js: -------------------------------------------------------------------------------- 1 | const { Network } = require('bcoin'); 2 | const { WalletClient } = require('bclient'); 3 | 4 | const network = Network.get('testnet'); 5 | 6 | const walletOptions = { 7 | network: network.type, 8 | port: network.walletPort, 9 | }; 10 | 11 | const walletClient = new WalletClient(walletOptions); 12 | const wallet = walletClient.wallet('primary'); 13 | 14 | async function preparePaymentAddress({ amount, address, callbackUrl }) { 15 | const newReceivingAddress = await wallet.createAddress('default'); 16 | console.log(newReceivingAddress); 17 | const paymentAddr = `bitcoin:${newReceivingAddress.address}?amount=${amount}`; 18 | console.log(paymentAddr); 19 | // TODO: add address to watch list 20 | return paymentAddr; 21 | } 22 | 23 | module.exports = { 24 | preparePaymentAddress, 25 | }; 26 | 27 | preparePaymentAddress({ 28 | amount: 0.0001, 29 | }); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tim Qian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | -------------------------------------------------------------------------------- /randomScripts/bcoin/spvNode.js: -------------------------------------------------------------------------------- 1 | // Get the SPV node object from the globally-installed bcoin package 2 | const bcoin = require('bcoin'); 3 | 4 | // Configure doc: https://github.com/bcoin-org/bcoin/blob/master/docs/Configuration.md 5 | const node = new bcoin.SPVNode({ 6 | // prefix: './bcoinData', // Bcoin's datadir KEY ALSO THERE??? yes, so user should backup HD master key first 7 | network: 'testnet', // testnet; regtest; simnet 8 | config: true, 9 | argv: true, 10 | env: true, 11 | logFile: true, 12 | logConsole: true, 13 | logLevel: 'debug', 14 | // db: 'leveldb', // This is default 15 | memory: false, 16 | persistent: true, 17 | workers: true, 18 | listen: true, 19 | loader: require, 20 | }); 21 | 22 | // Add wallet and database 23 | node.use(bcoin.wallet.plugin); 24 | 25 | (async () => { 26 | // Validate the prefix directory (probably ~/.bcoin) 27 | await node.ensure(); 28 | // Open the node and all its child objects, wait for the database to load 29 | await node.open(); 30 | // Connect to the network 31 | await node.connect(); 32 | 33 | // add wallet database 34 | // const walletdb = node.require('walletdb').wdb; 35 | // const wallet = await walletdb.primary; 36 | 37 | // write new transaction details to file named by tx hash 38 | node.on('tx', async (tx) => { 39 | console.log(tx); 40 | // TODO: when transaction arrive, check the txes cryptoo is monitoring 41 | // if matches: call the webhook 42 | }); 43 | 44 | node.on('block', (block) => { 45 | // console.log(block); 46 | }); 47 | // EVENTS: http://bcoin.io/guides/events.html 48 | 49 | node.plugins.walletdb.wdb.on('tx', (details, a) => { 50 | console.log(' -- wallet tx -- \n', details, a) 51 | }); 52 | // 53 | node.plugins.walletdb.wdb.on('balance', (details, a) => { 54 | console.log(' -- wallet balance -- \n', details, a) 55 | }); 56 | 57 | // see find comfirmed tx!!!!!! 58 | node.plugins.walletdb.wdb.on('confirmed', (details, a) => { 59 | console.log(' -- wallet confirmed -- \n', details, a) 60 | }); 61 | 62 | node.plugins.walletdb.wdb.on('address', (details) => { 63 | console.log(' -- wallet address -- \n', details) 64 | }); 65 | // Start the blockchain sync 66 | node.startSync(); 67 | })().catch((err) => { 68 | console.error(err.stack); 69 | process.exit(1); 70 | }); 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cryptoo 2 | 3 | Set up your own bitcoin payment system the easy way(with 4 APIs). 4 | 5 | ## Install 6 | 7 | ``` 8 | npm install cryptoo 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | const Cryptoo = require('cryptoo'); 15 | 16 | // Create a cryptoo instance 17 | // Start syncing blockchain data 18 | const cryptoo = new Cryptoo({ 19 | secret: 'sample-secret-e34dc9dff1b8b04c2b678ff7bb1dd02181bfe31b045f77', 20 | }); 21 | 22 | // Get recovery phrase to backup your HD wallet 23 | cryptoo.getRecoveryPhrase(); 24 | 25 | // Generate address to receive coins(shoud be recorded for looking up later) 26 | cryptoo.createAddress(); 27 | 28 | // Fires when an unconfirmed transaction is received to your address 29 | cryptoo.on('unconfirmedTx', ({ address, txHash, value }) => { 30 | console.log(`saw tx ${value} for ${address}`); 31 | }); 32 | 33 | // Fires when a transaction to your address has one confirmation 34 | cryptoo.on('confirmedTx', ({ address, txHash, value }) => { 35 | console.log(`received ${value} for ${address}`); 36 | }); 37 | ``` 38 | 39 | ## Configuration 40 | 41 | ```js 42 | new Cryptoo({ 43 | // default 'main', can also be 'testnet' 44 | network: 'main', 45 | // default '~/.bcoin', where to store blockchain and wallet data 46 | chainDataFolder: '~/.bcoin', 47 | // default: null, secret is used to protect your wallet api, so that if you open port 8332 accedently, other people wll not be able to access you wallet 48 | secret: 'v8ZgxMBicQKsPd1jmUQY2WBfrmK4tVMfAiSCh6xZXVMcNoDoyjLDRKe', 49 | // default: false, wether to show bcoin log in console, useful for debuging 50 | logBcoinLogInConsole: false, 51 | }); 52 | ``` 53 | 54 | ## Real-life examples 55 | 56 | - [Cryptoo landing page with bitcoin donation](https://getcryptoo.com); [[source code]](https://github.com/getcryptoo/cryptoo-example) 57 | - TO BE ADDED 58 | 59 | ## How does cryptoo work 60 | 61 | All the major functionlites of Cryptoo is provided by [bcoin](https://github.com/bcoin-org/bcoin), Cryptoo is a simple wrapper over [bcoin](https://github.com/bcoin-org/bcoin), exposing necessary apis to provide an easier api interface for user to build a payment system. It's about 160 lines of code. 62 | 63 | Bcoin is able to start and mantain bitcoin [SPV node](https://bitcoin.org/en/operating-modes-guide#simplified-payment-verification-spv)(light weight node) and also [HD wallet](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)(able to derive unlimited addresses from a seed key(recovery phrase)) 64 | 65 | When `Cryptoo` instance is created, it will start an SPV node and prepare an HD wallet for you. 66 | 67 | Depend on your network enviroment, it might take one to severial hours to sync the blockchain data, the blockchain data and wallet info will be stored in `chainDataFolder` you configure. 68 | ## About Security 69 | 70 | 1. **Close 8332 port**: for now cryptoo is a wrapper on bcoin, and bcoin will start an http server listening on this port for accessing wallet data, it's recommand to disable 8332 port for external access. 71 | 2. **Define a complex api-key**: if you are not able to close 8332 port, you should define a complex api-key 72 | 73 | ## Access your bitcoins 74 | 75 | As the wallet provided is an HD wallet, once you get the recovery phrase by invoking the `getRecoveryPhrase()` API. You can access you wallet by importing it to any wallet support bip44 76 | 77 | ### Recommend HD wallets 78 | - [Mycelium](https://wallet.mycelium.com/) 79 | - [Copay](https://copay.io/)(not able to recover wallet from phrase for testnet) 80 | 81 | ## Roadmap 82 | 83 | - cryptoo-eth 84 | 85 | ### Useful tools 86 | - faucet testnet bitcoin: http://bitcoinfaucet.uo1.net/send.php -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const bcoin = require('bcoin'); 2 | const { WalletClient, NodeClient } = require('bclient'); 3 | const EventEmitter = require('events'); 4 | const assert = require('assert'); 5 | 6 | class Cryptoo extends EventEmitter { 7 | /** 8 | * Create Cyptoo instace 9 | * @constructor 10 | * @param {Object} options 11 | * @param {String} [options.secret] apiKey to protect bcoin http endpoints 12 | * @param {String} [options.network="main"] default: "main"; bitcoin network: 'main' or 'testnet' 13 | * @param {String} [options.chainDataFolder="~/.bcoin"] 14 | * default: "~/.bcoin"; folder to store bcoin blockchain data 15 | * @param {Boolean} [options.logBcoinLogInConsole=false] whether print bcoin log in console 16 | */ 17 | constructor(options) { 18 | super(); 19 | 20 | const { 21 | secret, 22 | network = 'main', 23 | logBcoinLogInConsole = false, 24 | chainDataFolder = '~/.bcoin', 25 | } = options; 26 | 27 | assert(network === 'main' || network === 'testnet', 'network can only be "main" or "testnet"'); 28 | assert(typeof logBcoinLogInConsole === 'boolean', 'logBcoinLogInConsole should be Boolean type'); 29 | 30 | this.network = network; 31 | 32 | // Ref: https://github.com/bcoin-org/bcoin/blob/master/docs/Configuration.md 33 | this.node = new bcoin.SPVNode({ 34 | network, // testnet; main; 35 | apiKey: secret, 36 | logConsole: logBcoinLogInConsole, 37 | prefix: chainDataFolder, 38 | config: true, 39 | argv: true, 40 | env: true, 41 | logFile: true, 42 | logLevel: 'debug', 43 | memory: false, 44 | persistent: true, 45 | workers: true, 46 | listen: true, 47 | loader: require, 48 | }); 49 | 50 | const walletClient = new WalletClient({ 51 | network: bcoin.Network.get(network).type, 52 | port: bcoin.Network.get(network).walletPort, 53 | apiKey: secret, 54 | }); 55 | 56 | this.nodeClient = new NodeClient({ 57 | network: bcoin.Network.get(network).type, 58 | port: bcoin.Network.get(network).rpcPort, 59 | apiKey: secret, 60 | }); 61 | 62 | this.wallet = walletClient.wallet('primary'); 63 | this._initialized = this._init(); 64 | } 65 | 66 | /** 67 | * Initialize the node. 68 | */ 69 | async _init() { 70 | // Add wallet and database 71 | this.node.use(bcoin.wallet.plugin); 72 | // Validate the prefix directory (probably ~/.bcoin) 73 | await this.node.ensure(); 74 | // Open the node and all its child objects, wait for the database to load 75 | await this.node.open(); 76 | // Connect to the network 77 | await this.node.connect(); 78 | 79 | 80 | const chainInfo = await this.nodeClient.getInfo(); 81 | 82 | console.log(`[cryptoo]: syncing blockchain data: ${(chainInfo.chain.progress * 100).toFixed(3)}%`); 83 | 84 | // ref: https://bitcointalk.org/index.php?topic=2966580.0 85 | const genesisBlockTime = this.network === 'main' ? 1231006505 : 1296688602; 86 | const currentTime = Math.floor(Date.now() / 1000); 87 | 88 | this.node.on('connect', async (entry) => { 89 | const blockTime = entry.time; 90 | const progress = Math.min( 91 | 1, 92 | (blockTime - genesisBlockTime) / (currentTime - genesisBlockTime - 40 * 60), 93 | ); 94 | 95 | console.log(`[cryptoo]: syncing blockchain data: ${(progress * 100).toFixed(3)}%`); 96 | }); 97 | 98 | this.node.plugins.walletdb.wdb.on('tx', async (wallet, tx) => { 99 | const receivedOutputs = await this._getReceivedOutputs(tx); 100 | receivedOutputs.forEach(output => this.emit('unconfirmedTx', output)); 101 | }); 102 | 103 | this.node.plugins.walletdb.wdb.on('confirmed', async (wallet, tx) => { 104 | const receivedOutputs = await this._getReceivedOutputs(tx); 105 | receivedOutputs.forEach(output => this.emit('confirmedTx', output)); 106 | }); 107 | 108 | this.node.startSync(); 109 | } 110 | 111 | async _getReceivedOutputs(tx) { 112 | const txHash = tx.txid(); 113 | const txGotByClient = await this.wallet.getTX(txHash); 114 | return txGotByClient.outputs 115 | .filter(output => output.path) 116 | .map(output => ({ 117 | txHash, 118 | address: output.address, 119 | value: output.value, 120 | })); 121 | } 122 | 123 | /** 124 | * create a new address used to accept BTC 125 | */ 126 | async createAddress() { 127 | await this._initialized; 128 | const addr = await this.wallet.createAddress('default'); 129 | return addr.address; 130 | } 131 | 132 | /** 133 | * get recovery phrase 134 | */ 135 | async getRecoveryPhrase() { 136 | await this._initialized; 137 | const master = await this.wallet.getMaster(); 138 | return master.mnemonic.phrase; 139 | } 140 | } 141 | 142 | module.exports = Cryptoo; 143 | --------------------------------------------------------------------------------