├── migrate ├── index.js ├── latest └── README.md ├── test ├── data │ ├── tx1.raw │ ├── tx10.raw │ ├── tx2.raw │ ├── tx3.raw │ ├── tx4.raw │ ├── tx6.raw │ ├── tx7.raw │ ├── tx8.raw │ ├── tx9.raw │ ├── block1.raw │ ├── coin1.raw │ ├── headers1.raw │ ├── tx1-undo.raw │ ├── tx10-undo.raw │ ├── tx2-undo.raw │ ├── tx3-undo.raw │ ├── tx4-undo.raw │ ├── tx6-undo.raw │ ├── tx7-undo.raw │ ├── block300025.raw │ ├── block426884.raw │ ├── block898352.raw │ ├── block928828.raw │ ├── merkle300025.raw │ ├── compact426884.raw │ ├── compact898352.raw │ ├── block300025-undo.raw │ ├── block928828-undo.raw │ └── README.md ├── txmeta-test.js ├── mining-template-test.js ├── coin-test.js ├── util │ ├── reorg.js │ └── node-context.js ├── consensus-test.js ├── headers-test.js ├── mnemonic-test.js ├── keyring-test.js ├── coins-test.js └── outpoint-test.js ├── .gitignore ├── docs ├── Running-in-the-browser.md ├── Examples │ ├── fullnode.js │ ├── connect-to-peer.js │ ├── miner-configs.js │ ├── wallet.js │ ├── create-a-blockchain-and-mempool.js │ ├── peers-plugin.js │ ├── spv-sync-wallet.js │ ├── fullnode-and-wallet.js │ ├── get-tx-from-chain.js │ ├── create-sign-tx.js │ ├── connect-to-the-p2p-network.js │ └── client-api.js ├── Wallet-System.md ├── README.md └── Design.md ├── .npmignore ├── .babelrc ├── .eslintfiles ├── lib ├── hd │ ├── index.js │ ├── nfkd-compat.js │ ├── nfkd.js │ ├── words │ │ └── index.js │ ├── wordlist-browser.js │ ├── wordlist.js │ ├── README.md │ └── common.js ├── btc │ └── index.js ├── utils │ ├── index.js │ ├── binary.js │ └── util.js ├── workers │ ├── worker.js │ ├── index.js │ ├── framer.js │ ├── parent.js │ ├── parent-browser.js │ └── child-browser.js ├── mempool │ ├── index.js │ └── layout.js ├── coins │ ├── index.js │ └── undocoins.js ├── indexer │ ├── index.js │ ├── layout.js │ ├── nullclient.js │ └── txindexer.js ├── node │ └── index.js ├── mining │ ├── index.js │ ├── mine.js │ └── common.js ├── blockchain │ ├── index.js │ ├── layout.js │ └── common.js ├── net │ ├── seeds │ │ ├── index.js │ │ └── testnet.js │ ├── index.js │ ├── framer.js │ └── common.js ├── protocol │ ├── index.js │ ├── errors.js │ └── timedata.js ├── script │ ├── index.js │ ├── scripterror.js │ └── sigcache.js ├── primitives │ ├── index.js │ └── invitem.js ├── wallet │ ├── index.js │ ├── client.js │ ├── layout.js │ ├── plugin.js │ ├── common.js │ ├── nullclient.js │ └── node.js ├── pkg.js ├── types.js └── bcoin-browser.js ├── browser ├── debug.html ├── server.js ├── index.html └── src │ └── proxysocket.js ├── .editorconfig ├── bench ├── bench.js ├── script.js ├── merkle.js ├── mnemonic.js ├── coins.js ├── template-sort.js ├── chacha.js ├── walletdb.js └── tx.js ├── bin ├── cli ├── bwallet ├── wallet ├── bcash ├── spvnode └── node ├── etc ├── sample.wallet.conf └── sample.conf ├── Makefile ├── snap └── snapcraft.yaml ├── jsdoc.json ├── scripts ├── dump.js ├── seeds.sh ├── certs.sh └── gen.js ├── webpack.app.js ├── webpack.browser.js ├── webpack.compat.js ├── LICENSE ├── .circleci └── config.yml ├── README.md ├── .eslintrc.json └── package.json /migrate/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/data/tx1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx1.raw -------------------------------------------------------------------------------- /test/data/tx10.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx10.raw -------------------------------------------------------------------------------- /test/data/tx2.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx2.raw -------------------------------------------------------------------------------- /test/data/tx3.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx3.raw -------------------------------------------------------------------------------- /test/data/tx4.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx4.raw -------------------------------------------------------------------------------- /test/data/tx6.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx6.raw -------------------------------------------------------------------------------- /test/data/tx7.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx7.raw -------------------------------------------------------------------------------- /test/data/tx8.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx8.raw -------------------------------------------------------------------------------- /test/data/tx9.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx9.raw -------------------------------------------------------------------------------- /test/data/block1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block1.raw -------------------------------------------------------------------------------- /test/data/coin1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/coin1.raw -------------------------------------------------------------------------------- /test/data/headers1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/headers1.raw -------------------------------------------------------------------------------- /test/data/tx1-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx1-undo.raw -------------------------------------------------------------------------------- /test/data/tx10-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx10-undo.raw -------------------------------------------------------------------------------- /test/data/tx2-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx2-undo.raw -------------------------------------------------------------------------------- /test/data/tx3-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx3-undo.raw -------------------------------------------------------------------------------- /test/data/tx4-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx4-undo.raw -------------------------------------------------------------------------------- /test/data/tx6-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx6-undo.raw -------------------------------------------------------------------------------- /test/data/tx7-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/tx7-undo.raw -------------------------------------------------------------------------------- /test/data/block300025.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block300025.raw -------------------------------------------------------------------------------- /test/data/block426884.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block426884.raw -------------------------------------------------------------------------------- /test/data/block898352.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block898352.raw -------------------------------------------------------------------------------- /test/data/block928828.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block928828.raw -------------------------------------------------------------------------------- /test/data/merkle300025.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/merkle300025.raw -------------------------------------------------------------------------------- /test/data/compact426884.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/compact426884.raw -------------------------------------------------------------------------------- /test/data/compact898352.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/compact898352.raw -------------------------------------------------------------------------------- /test/data/block300025-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block300025-undo.raw -------------------------------------------------------------------------------- /test/data/block928828-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/HEAD/test/data/block928828-undo.raw -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | docs/reference/ 3 | docker_data/ 4 | browser/bcoin* 5 | npm-debug.log 6 | coverage/ 7 | -------------------------------------------------------------------------------- /migrate/latest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | console.error('No migrations available.'); 6 | process.exit(1); 7 | -------------------------------------------------------------------------------- /docs/Running-in-the-browser.md: -------------------------------------------------------------------------------- 1 | This page has been moved to [https://bcoin.io/guides/browser.html](https://bcoin.io/guides/browser.html) 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | bench/ 3 | docs/ 4 | docker_data/ 5 | test/ 6 | node_modules/ 7 | browser/bcoin* 8 | package-lock.json 9 | npm-debug.log 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "browsers": ["last 2 versions"] 6 | }, 7 | "useBuiltins": "usage", 8 | "loose": true 9 | }] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintfiles: -------------------------------------------------------------------------------- 1 | bench/ 2 | bin/cli 3 | bin/node 4 | bin/spvnode 5 | bin/wallet 6 | browser/server.js 7 | browser/wsproxy.js 8 | lib/ 9 | migrate/ 10 | scripts/ 11 | test/ 12 | webpack.browser.js 13 | webpack.compat.js 14 | -------------------------------------------------------------------------------- /lib/hd/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * hd/index.js - hd keys for bcoin 3 | * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | module.exports = require('./hd'); 10 | -------------------------------------------------------------------------------- /browser/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Use the console to access `global.bcoin`.
10 | 11 | 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 -------------------------------------------------------------------------------- /lib/btc/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * btc/index.js - high-level btc objects for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module btc 11 | */ 12 | 13 | exports.Amount = require('./amount'); 14 | exports.URI = require('./uri'); 15 | -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * utils/index.js - utils for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module utils 11 | */ 12 | 13 | exports.binary = require('./binary'); 14 | exports.fixed = require('./fixed'); 15 | exports.util = require('./util'); 16 | -------------------------------------------------------------------------------- /lib/workers/worker.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * worker.js - worker thread/process for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const Master = require('./master'); 11 | const server = new Master(); 12 | 13 | server.listen(); 14 | -------------------------------------------------------------------------------- /bench/bench.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function bench(name) { 4 | const start = process.hrtime(); 5 | return function end(ops) { 6 | const elapsed = process.hrtime(start); 7 | const time = elapsed[0] + elapsed[1] / 1e9; 8 | const rate = ops / time; 9 | 10 | console.log('%s: ops=%d, time=%d, rate=%s', 11 | name, ops, time, rate.toFixed(5)); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /bin/cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | console.error('%s%s', 6 | 'Warning: The `bcoin cli` interface is deprecated.\n', 7 | 'Please use `bcoin-cli` ($ npm install bclient).'); 8 | 9 | if (process.argv.length > 2 && process.argv[2] === 'wallet') { 10 | process.argv.splice(2, 1); // Evil hack. 11 | require('bclient/bin/bwallet-cli'); 12 | } else { 13 | require('bclient/bin/bcoin-cli'); 14 | } 15 | -------------------------------------------------------------------------------- /etc/sample.wallet.conf: -------------------------------------------------------------------------------- 1 | # Sample bcoin wallet config file (~/.bcoin/wallet.conf) 2 | 3 | # 4 | # Options 5 | # 6 | 7 | # network: main 8 | 9 | # 10 | # HTTP 11 | # 12 | 13 | http-host: :: 14 | # http-port: 8334 15 | # ssl: true 16 | # ssl-cert: @/ssl/cert.crt 17 | # ssl-key: @/ssl/priv.key 18 | api-key: bikeshed 19 | # no-auth: false 20 | # cors: false 21 | 22 | # 23 | # Wallet 24 | # 25 | 26 | checkpoints: true 27 | wallet-auth: false 28 | -------------------------------------------------------------------------------- /lib/mempool/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mempool/index.js - mempool for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module mempool 11 | */ 12 | 13 | exports.Fees = require('./fees'); 14 | exports.layout = require('./layout'); 15 | exports.MempoolEntry = require('./mempoolentry'); 16 | exports.Mempool = require('./mempool'); 17 | -------------------------------------------------------------------------------- /lib/coins/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * coins/index.js - utxo management for bcoin 3 | * Copyright (c) 2016-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module coins 11 | */ 12 | 13 | exports.Coins = require('./coins'); 14 | exports.CoinView = require('./coinview'); 15 | exports.compress = require('./compress'); 16 | exports.UndoCoins = require('./undocoins'); 17 | -------------------------------------------------------------------------------- /lib/indexer/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * index.js - indexer for bcoin 3 | * Copyright (c) 2018, the bcoin developers (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module indexer 11 | */ 12 | 13 | exports.Indexer = require('./indexer'); 14 | exports.TXIndexer = require('./txindexer'); 15 | exports.AddrIndexer = require('./addrindexer'); 16 | exports.ChainClient = require('./chainclient'); 17 | -------------------------------------------------------------------------------- /bench/script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const random = require('bcrypto/lib/random'); 4 | const Script = require('../lib/script/script'); 5 | const bench = require('./bench'); 6 | 7 | const hashes = []; 8 | 9 | for (let i = 0; i < 100000; i++) 10 | hashes.push(random.randomBytes(20)); 11 | 12 | { 13 | const end = bench('hash'); 14 | for (let i = 0; i < hashes.length; i++) 15 | Script.fromPubkeyhash(hashes[i]); 16 | end(100000); 17 | } 18 | -------------------------------------------------------------------------------- /lib/node/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * node/index.js - node for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module node 11 | */ 12 | 13 | exports.FullNode = require('./fullnode'); 14 | exports.HTTP = require('./http'); 15 | exports.Node = require('./node'); 16 | exports.RPC = require('./rpc'); 17 | exports.SPVNode = require('./spvnode'); 18 | -------------------------------------------------------------------------------- /lib/hd/nfkd-compat.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * nfkd-compat.js - unicode normalization for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const unorm = require('./unorm'); 10 | 11 | function nfkd(str) { 12 | if (str.normalize) 13 | return str.normalize('NFKD'); 14 | 15 | return unorm.nfkd(str); 16 | } 17 | 18 | /* 19 | * Expose 20 | */ 21 | 22 | module.exports = nfkd; 23 | -------------------------------------------------------------------------------- /lib/workers/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * workers/index.js - workers for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module workers 11 | */ 12 | 13 | exports.Framer = require('./framer'); 14 | exports.jobs = require('./jobs'); 15 | exports.packets = require('./packets'); 16 | exports.Parser = require('./parser'); 17 | exports.WorkerPool = require('./workerpool'); 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @npm run webpack 3 | 4 | app: 5 | @npm run webpack-app 6 | 7 | browser: 8 | @npm run webpack-browser 9 | 10 | compat: 11 | @npm run webpack-compat 12 | 13 | node: 14 | @npm run webpack-node 15 | 16 | browserify: 17 | @npm run browserify 18 | 19 | clean: 20 | @npm run clean 21 | 22 | docs: 23 | @npm run docs 24 | 25 | lint: 26 | @npm run lint 27 | 28 | test: 29 | @npm test 30 | 31 | .PHONY: all app browser compat node browserify clean docs lint test 32 | -------------------------------------------------------------------------------- /lib/mining/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mining/index.js - mining infrastructure for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module mining 11 | */ 12 | 13 | exports.common = require('./common'); 14 | exports.CPUMiner = require('./cpuminer'); 15 | exports.mine = require('./mine'); 16 | exports.Miner = require('./miner'); 17 | exports.BlockTemplate = require('./template'); 18 | -------------------------------------------------------------------------------- /lib/blockchain/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * blockchain/index.js - blockchain for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module blockchain 11 | */ 12 | 13 | exports.ChainDB = require('./chaindb'); 14 | exports.ChainEntry = require('./chainentry'); 15 | exports.Chain = require('./chain'); 16 | exports.common = require('./common'); 17 | exports.layout = require('./layout'); 18 | -------------------------------------------------------------------------------- /lib/hd/nfkd.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * nfkd.js - unicode normalization for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Normalize unicode string. 11 | * @alias module:utils.nfkd 12 | * @param {String} str 13 | * @returns {String} 14 | */ 15 | 16 | function nfkd(str) { 17 | return str.normalize('NFKD'); 18 | } 19 | 20 | /* 21 | * Expose 22 | */ 23 | 24 | module.exports = nfkd; 25 | -------------------------------------------------------------------------------- /lib/net/seeds/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * seeds.js - seeds for bcoin 3 | * Copyright (c) 2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const main = require('./main'); 10 | const testnet = require('./testnet'); 11 | 12 | exports.get = function get(type) { 13 | switch (type) { 14 | case 'main': 15 | return main; 16 | case 'testnet': 17 | return testnet; 18 | default: 19 | return []; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /migrate/README.md: -------------------------------------------------------------------------------- 1 | Bcash Migrations 2 | ================ 3 | 4 | There are no migrations necessary for bcash. Current database versions are as 5 | follows: 6 | - ChainDB - `v5` 7 | - WalletDB - `v7` 8 | - Mempool - `v0` 9 | - Indexer - `v0` 10 | 11 | *Note: Lastest version of bcoin does not have separate Indexer and its ChainDB 12 | is at `v4`(See [Refactor Indexers][bcoin-indexers]), but WalletDB is 13 | compatible.* 14 | 15 | [bcoin-indexers]: https://github.com/bcoin-org/bcoin/pull/424 16 | -------------------------------------------------------------------------------- /bench/merkle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const merkle = require('bcrypto/lib/merkle'); 5 | const random = require('bcrypto/lib/random'); 6 | const bench = require('./bench'); 7 | 8 | const leaves = []; 9 | 10 | for (let i = 0; i < 3000; i++) 11 | leaves.push(random.randomBytes(32)); 12 | 13 | { 14 | const end = bench('tree'); 15 | for (let i = 0; i < 1000; i++) { 16 | const [n, m] = merkle.createTree(leaves.slice()); 17 | assert(n); 18 | assert(!m); 19 | } 20 | end(1000); 21 | } 22 | -------------------------------------------------------------------------------- /lib/protocol/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * protocol/index.js - protocol constants for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module protocol 11 | */ 12 | 13 | exports.consensus = require('./consensus'); 14 | exports.errors = require('./errors'); 15 | exports.Network = require('./network'); 16 | exports.networks = require('./networks'); 17 | exports.policy = require('./policy'); 18 | exports.timedata = require('./timedata'); 19 | -------------------------------------------------------------------------------- /bench/mnemonic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const bench = require('./bench'); 5 | const HD = require('../lib/hd'); 6 | const Mnemonic = require('../lib/hd/mnemonic'); 7 | 8 | const mnemonic = new Mnemonic(); 9 | HD.fromMnemonic(mnemonic); 10 | 11 | const phrase = mnemonic.getPhrase(); 12 | 13 | assert.strictEqual(Mnemonic.fromPhrase(phrase).getPhrase(), phrase); 14 | 15 | { 16 | const end = bench('fromPhrase'); 17 | for (let i = 0; i < 10000; i++) 18 | Mnemonic.fromPhrase(phrase); 19 | end(10000); 20 | } 21 | -------------------------------------------------------------------------------- /lib/script/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * script/index.js - bitcoin scripting for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module script 11 | */ 12 | 13 | exports.common = require('./common'); 14 | exports.Opcode = require('./opcode'); 15 | exports.Script = require('./script'); 16 | exports.ScriptError = require('./scripterror'); 17 | exports.ScriptNum = require('./scriptnum'); 18 | exports.sigcache = require('./sigcache'); 19 | exports.Stack = require('./stack'); 20 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: bcoin 2 | version: git 3 | summary: A fullnode Bitcoin implementation for miners, wallets, and exchanges 4 | description: | 5 | Bcoin is an alternative implementation of the bitcoin protocol, written in 6 | node.js. 7 | 8 | grade: devel # must be 'stable' to release into candidate/stable channels 9 | confinement: strict 10 | 11 | apps: 12 | bcoin: 13 | command: bcoin 14 | plugs: [network, network-bind] 15 | 16 | parts: 17 | bcoin: 18 | source: . 19 | plugin: nodejs 20 | build-packages: [python, gcc] 21 | node-engine: 8.0.0 22 | -------------------------------------------------------------------------------- /lib/hd/words/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * index.js - wordlists for bcoin 3 | * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | exports.chinese = { 10 | simplified: require('./chinese-simplified.js'), 11 | traditional: require('./chinese-traditional.js') 12 | }; 13 | 14 | exports.english = require('./english.js'); 15 | exports.french = require('./french.js'); 16 | exports.italian = require('./italian.js'); 17 | exports.japanese = require('./japanese.js'); 18 | exports.spanish = require('./spanish.js'); 19 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "source": { 6 | "include": ["README.md", "lib/"], 7 | "exclude": [], 8 | "includePattern": ".+\\.js(doc)?$", 9 | "excludePattern": "(^|\\/|\\\\)_" 10 | }, 11 | "plugins": ["plugins/markdown"], 12 | "templates": { 13 | "cleverLinks": false, 14 | "monospaceLinks": false 15 | }, 16 | "opts": { 17 | "template": "templates/default", 18 | "encoding": "utf8", 19 | "destination": "./docs/reference", 20 | "recurse": true, 21 | "private": true, 22 | "pedantic": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/net/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * net/index.js - p2p for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module net 11 | */ 12 | 13 | exports.bip152 = require('./bip152'); 14 | exports.common = require('./common'); 15 | exports.Framer = require('./framer'); 16 | exports.HostList = require('./hostlist'); 17 | exports.NetAddress = require('./netaddress'); 18 | exports.packets = require('./packets'); 19 | exports.Parser = require('./parser'); 20 | exports.Peer = require('./peer'); 21 | exports.Pool = require('./pool'); 22 | -------------------------------------------------------------------------------- /docs/Examples/fullnode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | 5 | const node = new bcoin.FullNode({ 6 | memory: true, 7 | network: 'testnet', 8 | workers: true 9 | }); 10 | 11 | (async () => { 12 | await node.open(); 13 | await node.connect(); 14 | 15 | node.on('connect', (entry, block) => { 16 | console.log('%s (%d) added to chain.', entry.rhash(), entry.height); 17 | }); 18 | 19 | node.on('tx', (tx) => { 20 | console.log('%s added to mempool.', tx.txid()); 21 | }); 22 | 23 | node.startSync(); 24 | })().catch((err) => { 25 | console.error(err.stack); 26 | process.exit(1); 27 | }); 28 | -------------------------------------------------------------------------------- /lib/mempool/layout.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * layout.js - mempool data layout for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const bdb = require('bdb'); 10 | 11 | /* 12 | * Database Layout: 13 | * V -> db version 14 | * v -> serialization version 15 | * R -> tip hash 16 | * e[hash] -> entry 17 | */ 18 | 19 | const layout = { 20 | V: bdb.key('V'), 21 | v: bdb.key('v'), 22 | R: bdb.key('R'), 23 | F: bdb.key('F'), 24 | e: bdb.key('e', ['hash256']) 25 | }; 26 | 27 | /* 28 | * Expose 29 | */ 30 | 31 | module.exports = layout; 32 | -------------------------------------------------------------------------------- /lib/indexer/layout.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * layout.js - indexer layout for bcoin 3 | * Copyright (c) 2018, the bcoin developers (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const bdb = require('bdb'); 10 | 11 | /* 12 | * Index Database Layout: 13 | * To be extended by indexer implementations 14 | * V -> db version 15 | * O -> flags 16 | * h[height] -> recent block hash 17 | * R -> chain sync state 18 | */ 19 | 20 | const layout = { 21 | V: bdb.key('V'), 22 | O: bdb.key('O'), 23 | h: bdb.key('h', ['uint32']), 24 | R: bdb.key('R') 25 | }; 26 | 27 | /* 28 | * Expose 29 | */ 30 | 31 | module.exports = layout; 32 | -------------------------------------------------------------------------------- /scripts/dump.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const heapdump = require('heapdump'); 4 | const MempoolEntry = require('../lib/mempool/mempoolentry'); 5 | const Coins = require('../lib/coins/coins'); 6 | const common = require('../test/util/common'); 7 | 8 | const [tx, view] = common.readTX('tx4').getTX(); 9 | const coins = Coins.fromTX(tx, 0); 10 | const entry = MempoolEntry.fromTX(tx, view, 1000000); 11 | 12 | setInterval(() => { 13 | console.log(tx.hash('hex')); 14 | console.log(coins.outputs.length); 15 | console.log(entry.tx); 16 | }, 60 * 1000); 17 | 18 | setImmediate(() => { 19 | heapdump.writeSnapshot(`${__dirname}/../dump.heapsnapshot`, (err) => { 20 | if (err) 21 | throw err; 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /lib/hd/wordlist-browser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * wordlist.js - wordlists for bcoin 3 | * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const words = require('./words'); 10 | 11 | exports.get = function get(name) { 12 | switch (name) { 13 | case 'simplified chinese': 14 | return words.chinese.simplified; 15 | case 'traditional chinese': 16 | return words.chinese.traditional; 17 | case 'english': 18 | return words.english; 19 | case 'french': 20 | return words.french; 21 | case 'italian': 22 | return words.italian; 23 | case 'japanese': 24 | return words.japanese; 25 | case 'spanish': 26 | return words.spanish; 27 | default: 28 | throw new Error(`Unknown language: ${name}.`); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /test/txmeta-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const assert = require('./util/assert'); 7 | const Network = require('../lib/protocol/network'); 8 | const TXMeta = require('../lib/primitives/txmeta'); 9 | 10 | const network = Network.get('regtest'); 11 | 12 | describe('TXMeta', function() { 13 | it('should return JSON for txmeta', async () => { 14 | // unconfirmed at height 100 15 | const txmeta1 = new TXMeta(); 16 | const txJSON1 = txmeta1.getJSON(network, null, 100); 17 | assert.strictEqual(txJSON1.confirmations, 0); 18 | 19 | // confirmed once at height 100 20 | const txmeta2 = TXMeta.fromOptions( {height: 100} ); 21 | txmeta2.height = 100; 22 | const txJSON2 = txmeta2.getJSON(network, null, 100); 23 | assert.strictEqual(txJSON2.confirmations, 1); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /webpack.app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Path = require('path'); 4 | const webpack = require('webpack'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const str = JSON.stringify; 7 | const env = process.env; 8 | 9 | module.exports = { 10 | target: 'web', 11 | entry: { 12 | 'app': './browser/src/app', 13 | 'worker': './lib/workers/worker' 14 | }, 15 | output: { 16 | path: Path.join(__dirname, 'browser'), 17 | filename: '[name].js' 18 | }, 19 | resolve: { 20 | modules: ['node_modules'], 21 | extensions: ['-browser.js', '.js', '.json'] 22 | }, 23 | plugins: [ 24 | new webpack.DefinePlugin({ 25 | 'process.env.BCASH_NETWORK': 26 | str(env.BCASH_NETWORK || 'main'), 27 | 'process.env.BCASH_WORKER_FILE': 28 | str(env.BCASH_WORKER_FILE || '/bcash-worker.js') 29 | }), 30 | new UglifyJsPlugin() 31 | ] 32 | }; 33 | -------------------------------------------------------------------------------- /bench/coins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const CoinView = require('../lib/coins/coinview'); 4 | const BufferReader = require('../lib/utils/reader'); 5 | const StaticWriter = require('../lib/utils/writer'); 6 | const common = require('../test/util/common'); 7 | const bench = require('./bench'); 8 | 9 | const [tx, view] = common.readTX('tx3').getTX(); 10 | 11 | { 12 | const end = bench('serialize'); 13 | 14 | for (let i = 0; i < 10000000; i++) { 15 | const bw = new StaticWriter(view.getSize(tx)); 16 | view.toWriter(bw, tx).render(); 17 | } 18 | 19 | end(10000000); 20 | } 21 | 22 | { 23 | const bw = new StaticWriter(view.getSize(tx)); 24 | const raw = view.toWriter(bw, tx).render(); 25 | 26 | const end = bench('parse'); 27 | 28 | for (let i = 0; i < 10000000; i++) { 29 | const br = new BufferReader(raw); 30 | CoinView.fromReader(br, tx); 31 | } 32 | 33 | end(10000000); 34 | } 35 | -------------------------------------------------------------------------------- /lib/hd/wordlist.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * wordlist.js - wordlists for bcoin 3 | * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | exports.get = function get(name) { 10 | switch (name) { 11 | case 'simplified chinese': 12 | return require('./words/chinese-simplified.js'); 13 | case 'traditional chinese': 14 | return require('./words/chinese-traditional.js'); 15 | case 'english': 16 | return require('./words/english.js'); 17 | case 'french': 18 | return require('./words/french.js'); 19 | case 'italian': 20 | return require('./words/italian.js'); 21 | case 'japanese': 22 | return require('./words/japanese.js'); 23 | case 'spanish': 24 | return require('./words/spanish.js'); 25 | default: 26 | throw new Error(`Unknown language: ${name}.`); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /lib/primitives/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * primitives/index.js - bitcoin primitives for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module primitives 11 | */ 12 | 13 | exports.AbstractBlock = require('./abstractblock'); 14 | exports.Address = require('./address'); 15 | exports.Block = require('./block'); 16 | exports.Coin = require('./coin'); 17 | exports.Headers = require('./headers'); 18 | exports.Input = require('./input'); 19 | exports.InvItem = require('./invitem'); 20 | exports.KeyRing = require('./keyring'); 21 | exports.MemBlock = require('./memblock'); 22 | exports.MerkleBlock = require('./merkleblock'); 23 | exports.MTX = require('./mtx'); 24 | exports.Outpoint = require('./outpoint'); 25 | exports.Output = require('./output'); 26 | exports.TX = require('./tx'); 27 | exports.TXMeta = require('./txmeta'); 28 | -------------------------------------------------------------------------------- /lib/wallet/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * wallet/index.js - wallet for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * @module wallet 11 | */ 12 | 13 | exports.Account = require('./account'); 14 | exports.Client = require('./client'); 15 | exports.common = require('./common'); 16 | exports.HTTP = require('./http'); 17 | exports.layout = require('./layout'); 18 | exports.MasterKey = require('./masterkey'); 19 | exports.NodeClient = require('./nodeclient'); 20 | exports.Path = require('./path'); 21 | exports.plugin = require('./plugin'); 22 | exports.records = require('./records'); 23 | exports.RPC = require('./rpc'); 24 | exports.Node = require('./node'); 25 | exports.TXDB = require('./txdb'); 26 | exports.WalletDB = require('./walletdb'); 27 | exports.Wallet = require('./wallet'); 28 | exports.WalletKey = require('./walletkey'); 29 | -------------------------------------------------------------------------------- /webpack.browser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Path = require('path'); 4 | const webpack = require('webpack'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const str = JSON.stringify; 7 | const env = process.env; 8 | 9 | module.exports = { 10 | target: 'web', 11 | entry: { 12 | 'bcash': './lib/bcash', 13 | 'bcash-worker': './lib/workers/worker' 14 | }, 15 | output: { 16 | library: 'bcash', 17 | libraryTarget: 'umd', 18 | path: Path.join(__dirname, 'browser'), 19 | filename: '[name].js' 20 | }, 21 | resolve: { 22 | modules: ['node_modules'], 23 | extensions: ['-browser.js', '.js', '.json'] 24 | }, 25 | plugins: [ 26 | new webpack.DefinePlugin({ 27 | 'process.env.BCASH_NETWORK': 28 | str(env.BCASH_NETWORK || 'main'), 29 | 'process.env.BCASH_WORKER_FILE': 30 | str(env.BCASH_WORKER_FILE || '/bcash-worker.js') 31 | }), 32 | new UglifyJsPlugin() 33 | ] 34 | }; 35 | -------------------------------------------------------------------------------- /lib/workers/framer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * workers.js - worker processes for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const bio = require('bufio'); 11 | 12 | /** 13 | * Framer 14 | * @alias module:workers.Framer 15 | */ 16 | 17 | class Framer { 18 | /** 19 | * Create a framer. 20 | * @constructor 21 | */ 22 | 23 | constructor() {} 24 | 25 | packet(payload) { 26 | const size = 10 + payload.getSize(); 27 | const bw = bio.write(size); 28 | 29 | bw.writeU32(payload.id); 30 | bw.writeU8(payload.cmd); 31 | bw.seek(4); 32 | 33 | payload.toWriter(bw); 34 | 35 | bw.writeU8(0x0a); 36 | 37 | const msg = bw.render(); 38 | msg.writeUInt32LE(msg.length - 10, 5, true); 39 | 40 | return msg; 41 | } 42 | } 43 | 44 | /* 45 | * Expose 46 | */ 47 | 48 | module.exports = Framer; 49 | -------------------------------------------------------------------------------- /scripts/seeds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir=$(dirname "$(which "$0")") 4 | url_main='https://raw.githubusercontent.com/Bitcoin-ABC/bitcoin-abc/master/contrib/seeds/nodes_main.txt' 5 | url_testnet='https://raw.githubusercontent.com/Bitcoin-ABC/bitcoin-abc/master/contrib/seeds/nodes_test.txt' 6 | 7 | getseeds() { 8 | echo "$(curl -s "$1")" 9 | } 10 | 11 | tojs() { 12 | local data=$(cat) 13 | local body=$(echo "$data" | head -n -1) 14 | local last=$(echo "$data" | tail -n 1) 15 | echo "'use strict';" 16 | echo '' 17 | echo 'module.exports = [' 18 | echo "$body" | while read line; do 19 | if echo "$line" | grep '^#' > /dev/null; then 20 | continue 21 | fi 22 | if echo "$line" | grep '^ *$' > /dev/null; then 23 | continue 24 | fi 25 | echo " '${line}'," 26 | done 27 | echo " '${last}'" 28 | echo '];' 29 | } 30 | 31 | getseeds "$url_main" | tojs > "${dir}/../lib/net/seeds/main.js" 32 | getseeds "$url_testnet" | tojs > "${dir}/../lib/net/seeds/testnet.js" 33 | -------------------------------------------------------------------------------- /docs/Examples/connect-to-peer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Usage: $ node ./docs/Examples/connect-to-peer.js [ip]:[port] 4 | 5 | const bcoin = require('../..'); 6 | const network = bcoin.Network.get('testnet'); 7 | 8 | const peer = bcoin.Peer.fromOptions({ 9 | network: 'testnet', 10 | agent: 'my-subversion', 11 | hasWitness: () => { 12 | return false; 13 | } 14 | }); 15 | 16 | const addr = bcoin.net.NetAddress.fromHostname(process.argv[2], 'testnet'); 17 | 18 | console.log(`Connecting to ${addr.hostname}`); 19 | 20 | peer.connect(addr); 21 | peer.tryOpen(); 22 | 23 | peer.on('error', (err) => { 24 | console.error(err); 25 | }); 26 | 27 | peer.on('packet', (msg) => { 28 | console.log(msg); 29 | 30 | if (msg.cmd === 'block') { 31 | console.log('Block!'); 32 | console.log(msg.block.toBlock()); 33 | return; 34 | } 35 | 36 | if (msg.cmd === 'inv') { 37 | peer.getData(msg.items); 38 | return; 39 | } 40 | }); 41 | 42 | peer.on('open', () => { 43 | peer.getBlock([network.genesis.hash]); 44 | }); 45 | -------------------------------------------------------------------------------- /docs/Examples/miner-configs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | 5 | const key = bcoin.wallet.WalletKey.generate('regtest'); 6 | 7 | const workers = new bcoin.WorkerPool({ 8 | enabled: true 9 | }); 10 | 11 | const chain = new bcoin.Chain({ 12 | network: 'regtest', 13 | workers: workers 14 | }); 15 | 16 | const miner = new bcoin.Miner({ 17 | chain: chain, 18 | addresses: [key.getAddress()], 19 | coinbaseFlags: 'my-miner', 20 | workers: workers 21 | }); 22 | 23 | (async () => { 24 | await miner.open(); 25 | 26 | const tmpl = await miner.createBlock(); 27 | 28 | console.log('Block template:'); 29 | console.log(tmpl); 30 | 31 | const job = await miner.createJob(); 32 | const block = await job.mineAsync(); 33 | 34 | console.log('Mined block:'); 35 | console.log(block); 36 | console.log(block.txs[0]); 37 | 38 | await chain.add(block); 39 | 40 | console.log('New tip:'); 41 | console.log(chain.tip); 42 | })().catch((err) => { 43 | console.error(err.stack); 44 | process.exit(1); 45 | }); 46 | -------------------------------------------------------------------------------- /browser/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bweb = require('bweb'); 4 | const fs = require('bfile'); 5 | const WSProxy = require('./wsproxy'); 6 | 7 | const index = fs.readFileSync(`${__dirname}/index.html`); 8 | const app = fs.readFileSync(`${__dirname}/app.js`); 9 | const worker = fs.readFileSync(`${__dirname}/worker.js`); 10 | 11 | const proxy = new WSProxy({ 12 | ports: [8333, 18333, 18444, 28333, 28901] 13 | }); 14 | 15 | const server = bweb.server({ 16 | port: Number(process.argv[2]) || 8080, 17 | sockets: false 18 | }); 19 | 20 | server.use(server.router()); 21 | 22 | proxy.on('error', (err) => { 23 | console.error(err.stack); 24 | }); 25 | 26 | server.on('error', (err) => { 27 | console.error(err.stack); 28 | }); 29 | 30 | server.get('/', (req, res) => { 31 | res.send(200, index, 'html'); 32 | }); 33 | 34 | server.get('/app.js', (req, res) => { 35 | res.send(200, app, 'js'); 36 | }); 37 | 38 | server.get('/worker.js', (req, res) => { 39 | res.send(200, worker, 'js'); 40 | }); 41 | 42 | proxy.attach(server.http); 43 | 44 | server.open(); 45 | -------------------------------------------------------------------------------- /webpack.compat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Path = require('path'); 4 | const webpack = require('webpack'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const str = JSON.stringify; 7 | const env = process.env; 8 | 9 | module.exports = { 10 | target: 'web', 11 | entry: { 12 | 'bcash': './lib/bcash', 13 | 'bcash-worker': './lib/workers/worker' 14 | }, 15 | output: { 16 | library: 'bcash', 17 | libraryTarget: 'umd', 18 | path: Path.join(__dirname, 'browser'), 19 | filename: '[name].js' 20 | }, 21 | resolve: { 22 | modules: ['node_modules'], 23 | extensions: ['-compat.js', '-browser.js', '.js', '.json'] 24 | }, 25 | module: { 26 | rules: [{ 27 | test: /\.js$/, 28 | loader: 'babel-loader' 29 | }] 30 | }, 31 | plugins: [ 32 | new webpack.DefinePlugin({ 33 | 'process.env.BCASH_NETWORK': 34 | str(env.BCASH_NETWORK || 'main'), 35 | 'process.env.BCASH_WORKER_FILE': 36 | str(env.BCASH_WORKER_FILE || '/bcash-worker.js') 37 | }), 38 | new UglifyJsPlugin() 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /docs/Wallet-System.md: -------------------------------------------------------------------------------- 1 | Wallet REST API: [REST-RPC-API](REST-RPC-API.md) 2 | 3 | ## Notes on wallet system 4 | 5 | Bcoin maintains a wallet database which contains every wallet. Wallets are _not 6 | usable_ without also using a wallet database. For testing, the wallet database 7 | can be in-memory, but it must be there. 8 | 9 | Wallets in bcash use bip44. They also originally supported bip45 for multisig, 10 | but support was removed to reduce code complexity, and also because bip45 11 | doesn't seem to add any benefit in practice. 12 | 13 | The wallet database can contain many different wallets, with many different 14 | accounts, with many different addresses for each account. Bcoin should 15 | theoretically be able to scale to hundreds of thousands of 16 | wallets/accounts/addresses. 17 | 18 | Each account can be of a different type. You could have a pubkeyhash account, 19 | as well as a multisig account, a witness pubkeyhash account, etc. 20 | 21 | Note that accounts should not be accessed directly from the public API. They do 22 | not have locks which can lead to race conditions during writes. 23 | -------------------------------------------------------------------------------- /docs/Examples/wallet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | const random = require('bcrypto/lib/random'); 5 | 6 | function dummy() { 7 | const hash = random.randomBytes(32); 8 | return new bcoin.Outpoint(hash, 0); 9 | } 10 | 11 | const walletdb = new bcoin.wallet.WalletDB({ 12 | network: 'testnet', 13 | db: 'memory' 14 | }); 15 | 16 | (async () => { 17 | await walletdb.open(); 18 | 19 | const wallet = await walletdb.create(); 20 | 21 | console.log('Created wallet'); 22 | console.log(wallet); 23 | 24 | const acct = await wallet.createAccount({ 25 | name: 'foo' 26 | }); 27 | 28 | console.log('Created account'); 29 | console.log(acct); 30 | 31 | const mtx = new bcoin.MTX(); 32 | mtx.addOutpoint(dummy()); 33 | mtx.addOutput(acct.receiveAddress(), 50460); 34 | 35 | const tx = mtx.toTX(); 36 | 37 | await walletdb.addTX(tx); 38 | 39 | const wtx = await wallet.getTX(tx.hash()); 40 | 41 | console.log('Added transaction'); 42 | console.log(wtx); 43 | })().catch((err) => { 44 | console.error(err.stack); 45 | process.exit(1); 46 | }); 47 | -------------------------------------------------------------------------------- /bin/bwallet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rl=0 4 | daemon=0 5 | 6 | if ! type perl > /dev/null 2>& 1; then 7 | if uname | grep -i 'darwin' > /dev/null; then 8 | echo 'Bcoin requires perl to start on OSX.' >& 2 9 | exit 1 10 | fi 11 | rl=1 12 | fi 13 | 14 | if test $rl -eq 1; then 15 | file=$(readlink -f "$0") 16 | else 17 | # Have to do it this way 18 | # because OSX isn't a real OS 19 | file=$(perl -MCwd -e "print Cwd::realpath('$0')") 20 | fi 21 | 22 | dir=$(dirname "$file") 23 | 24 | if test x"$1" = x'cli'; then 25 | shift 26 | exec "${dir}/cli" "wallet" "$@" 27 | exit 1 28 | fi 29 | 30 | for arg in "$@"; do 31 | case "$arg" in 32 | --daemon) 33 | daemon=1 34 | ;; 35 | esac 36 | done 37 | 38 | if test $daemon -eq 1; then 39 | # And yet again, OSX doesn't support something. 40 | if ! type setsid > /dev/null 2>& 1; then 41 | ( 42 | "${dir}/wallet" "$@" > /dev/null 2>& 1 & 43 | echo "$!" 44 | ) 45 | exit 0 46 | fi 47 | ( 48 | setsid "${dir}/wallet" "$@" > /dev/null 2>& 1 & 49 | echo "$!" 50 | ) 51 | exit 0 52 | else 53 | exec "${dir}/wallet" "$@" 54 | exit 1 55 | fi 56 | -------------------------------------------------------------------------------- /bin/wallet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | process.title = 'bcash-wallet'; 6 | 7 | if (process.argv.indexOf('--help') !== -1 8 | || process.argv.indexOf('-h') !== -1) { 9 | console.error('See the bcoin docs at: https://github.com/bcoin-org/bcoin/tree/master/docs.'); 10 | process.exit(1); 11 | throw new Error('Could not exit.'); 12 | } 13 | 14 | if (process.argv.indexOf('--version') !== -1 15 | || process.argv.indexOf('-v') !== -1) { 16 | const pkg = require('../package.json'); 17 | console.log(pkg.version); 18 | process.exit(0); 19 | throw new Error('Could not exit.'); 20 | } 21 | 22 | const Node = require('../lib/wallet/node'); 23 | 24 | const node = new Node({ 25 | file: true, 26 | argv: true, 27 | env: true, 28 | logFile: true, 29 | logConsole: true, 30 | logLevel: 'debug', 31 | memory: false, 32 | workers: true, 33 | listen: true, 34 | loader: require 35 | }); 36 | 37 | process.on('unhandledRejection', (err, promise) => { 38 | throw err; 39 | }); 40 | 41 | process.on('SIGINT', async () => { 42 | await node.close(); 43 | }); 44 | 45 | (async () => { 46 | await node.ensure(); 47 | await node.open(); 48 | })().catch((err) => { 49 | console.error(err.stack); 50 | process.exit(1); 51 | }); 52 | -------------------------------------------------------------------------------- /test/data/README.md: -------------------------------------------------------------------------------- 1 | Multiple test vectors are based on Bitcoin Core. 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2009-2017 The Bitcoin Core developers 6 | Copyright (c) 2009-2017 Bitcoin Developers 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright (c) 2014-2015, Fedor Indutny (https://github.com/indutny) 4 | 5 | Copyright (c) 2014-2017, Christopher Jeffrey (https://github.com/chjj) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /docs/Examples/create-a-blockchain-and-mempool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bcoin = require('../..'); 3 | 4 | // Default network (so we can avoid passing 5 | // the `network` option into every object below.) 6 | bcoin.set('regtest'); 7 | 8 | // Start up a blockchain, mempool, and miner using in-memory 9 | // databases (stored in a red-black tree instead of on-disk). 10 | const chain = new bcoin.Chain({ network: 'regtest', memory: true }); 11 | const mempool = new bcoin.Mempool({ chain: chain }); 12 | const miner = new bcoin.Miner({ 13 | chain: chain, 14 | mempool: mempool, 15 | 16 | // Make sure miner won't block the main thread. 17 | useWorkers: true 18 | }); 19 | 20 | (async () => { 21 | // Open the chain 22 | await chain.open(); 23 | 24 | // Open the miner (initialize the databases, etc). 25 | // Miner will implicitly call `open` on mempool. 26 | await miner.open(); 27 | 28 | // Create a Cpu miner job 29 | const job = await miner.createJob(); 30 | 31 | // run miner 32 | const block = await job.mineAsync(); 33 | 34 | // Add the block to the chain 35 | console.log('Adding %s to the blockchain.', block.rhash); 36 | console.log(block); 37 | await chain.add(block); 38 | console.log('Added block!'); 39 | })().catch((err) => { 40 | console.error(err.stack); 41 | process.exit(1); 42 | }); 43 | -------------------------------------------------------------------------------- /bench/template-sort.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const random = require('bcrypto/lib/random'); 4 | const BlockTemplate = require('../lib/mining/template'); 5 | const util = require('../lib/utils/util'); 6 | const bench = require('./bench'); 7 | 8 | class MockTX { 9 | constructor() { 10 | this._hash = random.randomBytes(32); 11 | this._hhash = null; 12 | } 13 | 14 | hash() { 15 | return this._hash; 16 | } 17 | 18 | txid() { 19 | let h = this._hhash; 20 | 21 | if (!h) { 22 | h = util.revHex(this._hash); 23 | this._hhash = h; 24 | } 25 | 26 | return h; 27 | } 28 | 29 | getPriority() { 30 | return 0; 31 | } 32 | 33 | getSigopsCount() { 34 | return 0; 35 | } 36 | 37 | getFee() { 38 | return 0; 39 | } 40 | 41 | getRate() { 42 | return 0; 43 | } 44 | 45 | getSize() { 46 | return 32; 47 | } 48 | } 49 | 50 | const N = 100000; 51 | const template = new BlockTemplate(); 52 | const transactions = []; 53 | 54 | for (let i = 0; i < N; i++) 55 | transactions.push(new MockTX()); 56 | 57 | { 58 | const end = bench('create-push-entries'); 59 | for (let i = 0; i < N; i++) 60 | template.pushTX(transactions[i]); 61 | 62 | end(N); 63 | } 64 | 65 | { 66 | const end = bench('sort-transactions'); 67 | 68 | template.sort(); 69 | 70 | end(N); 71 | } 72 | -------------------------------------------------------------------------------- /docs/Examples/peers-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | 5 | function MyPlugin(node) { 6 | this.node = node; 7 | } 8 | 9 | MyPlugin.id = 'my-plugin'; 10 | 11 | MyPlugin.init = function init(node) { 12 | return new MyPlugin(node); 13 | }; 14 | 15 | MyPlugin.prototype.open = function open() { 16 | console.log('Opened my plugin.'); 17 | return Promise.resolve(); 18 | }; 19 | 20 | MyPlugin.prototype.close = function close() { 21 | console.log('Closed my plugin.'); 22 | return Promise.resolve(); 23 | }; 24 | 25 | MyPlugin.prototype.sayPeers = function sayPeers() { 26 | console.log('Number of peers: %d', this.node.pool.peers.size()); 27 | }; 28 | 29 | const node = new bcoin.FullNode({ 30 | memory: true, 31 | network: 'testnet', 32 | workers: true 33 | }); 34 | 35 | node.use(MyPlugin); 36 | 37 | (async () => { 38 | const plugin = node.require('my-plugin'); 39 | 40 | await node.open(); 41 | 42 | await node.connect(); 43 | 44 | plugin.sayPeers(); 45 | 46 | node.on('connect', (entry, block) => { 47 | console.log('%s (%d) added to chain.', entry.rhash(), entry.height); 48 | }); 49 | 50 | node.on('tx', (tx) => { 51 | console.log('%s added to mempool.', tx.txid()); 52 | }); 53 | 54 | node.startSync(); 55 | })().catch((err) => { 56 | console.error(err.stack); 57 | process.exit(1); 58 | }); 59 | -------------------------------------------------------------------------------- /lib/blockchain/layout.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * layout.js - blockchain data layout for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const bdb = require('bdb'); 10 | 11 | /* 12 | * Database Layout: 13 | * V -> db version 14 | * O -> chain options 15 | * R -> tip hash 16 | * D -> versionbits deployments 17 | * e[hash] -> entry 18 | * h[hash] -> height 19 | * H[height] -> hash 20 | * n[hash] -> next hash 21 | * p[hash] -> tip index 22 | * b[hash] -> block 23 | * c[hash] -> coins 24 | * u[hash] -> undo coins 25 | * v[bit][hash] -> versionbits state 26 | */ 27 | 28 | const layout = { 29 | V: bdb.key('V'), 30 | O: bdb.key('O'), 31 | R: bdb.key('R'), 32 | D: bdb.key('D'), 33 | e: bdb.key('e', ['hash256']), 34 | h: bdb.key('h', ['hash256']), 35 | H: bdb.key('H', ['uint32']), 36 | n: bdb.key('n', ['hash256']), 37 | p: bdb.key('p', ['hash256']), 38 | b: bdb.key('b', ['hash256']), 39 | t: bdb.key('t', ['hash256']), 40 | c: bdb.key('c', ['hash256', 'uint32']), 41 | u: bdb.key('u', ['hash256']), 42 | v: bdb.key('v', ['uint8', 'hash256']), 43 | T: bdb.key('T', ['hash', 'hash256']), 44 | C: bdb.key('C', ['hash', 'hash256', 'uint32']) 45 | }; 46 | 47 | /* 48 | * Expose 49 | */ 50 | 51 | module.exports = layout; 52 | -------------------------------------------------------------------------------- /docs/Examples/spv-sync-wallet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | 5 | bcoin.set('testnet'); 6 | 7 | // SPV chains only store the chain headers. 8 | const chain = new bcoin.Chain({ 9 | memory: false, 10 | location: '/tmp/bcoin/spvchain', 11 | spv: true 12 | }); 13 | 14 | const pool = new bcoin.Pool({ 15 | chain: chain, 16 | spv: true, 17 | maxPeers: 8 18 | }); 19 | 20 | const walletdb = new bcoin.wallet.WalletDB({ memory: true }); 21 | 22 | (async () => { 23 | await pool.open(); 24 | await walletdb.open(); 25 | 26 | const wallet = await walletdb.create(); 27 | 28 | console.log('Created wallet with address %s', await wallet.receiveAddress()); 29 | 30 | // Add our address to the spv filter. 31 | pool.watchAddress(await wallet.receiveAddress()); 32 | 33 | // Connect, start retrieving and relaying txs 34 | await pool.connect(); 35 | 36 | // Start the blockchain sync. 37 | pool.startSync(); 38 | 39 | pool.on('tx', async (tx) => { 40 | console.log('received TX'); 41 | 42 | await walletdb.addTX(tx); 43 | console.log('Transaction added to walletDB'); 44 | }); 45 | 46 | wallet.on('balance', (balance) => { 47 | console.log('Balance updated.'); 48 | console.log(bcoin.amount.btc(balance.unconfirmed)); 49 | }); 50 | })().catch((err) => { 51 | console.error(err.stack); 52 | process.exit(1); 53 | }); 54 | -------------------------------------------------------------------------------- /bin/bcash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rl=0 4 | daemon=0 5 | cmd='node' 6 | 7 | if ! type perl > /dev/null 2>& 1; then 8 | if uname | grep -i 'darwin' > /dev/null; then 9 | echo 'Bcoin requires perl to start on OSX.' >& 2 10 | exit 1 11 | fi 12 | rl=1 13 | fi 14 | 15 | if test $rl -eq 1; then 16 | file=$(readlink -f "$0") 17 | else 18 | # Have to do it this way 19 | # because OSX isn't a real OS 20 | file=$(perl -MCwd -e "print Cwd::realpath('$0')") 21 | fi 22 | 23 | dir=$(dirname "$file") 24 | 25 | if test x"$1" = x'cli'; then 26 | shift 27 | exec "${dir}/cli" "$@" 28 | exit 1 29 | fi 30 | 31 | if test x"$1" = x'wallet'; then 32 | exec "${dir}/cli" "$@" 33 | exit 1 34 | fi 35 | 36 | if test x"$1" = x'rpc'; then 37 | exec "${dir}/cli" "$@" 38 | exit 1 39 | fi 40 | 41 | for arg in "$@"; do 42 | case "$arg" in 43 | --daemon) 44 | daemon=1 45 | ;; 46 | --spv) 47 | cmd='spvnode' 48 | ;; 49 | esac 50 | done 51 | 52 | if test $daemon -eq 1; then 53 | # And yet again, OSX doesn't support something. 54 | if ! type setsid > /dev/null 2>& 1; then 55 | ( 56 | "${dir}/${cmd}" "$@" > /dev/null 2>& 1 & 57 | echo "$!" 58 | ) 59 | exit 0 60 | fi 61 | ( 62 | setsid "${dir}/${cmd}" "$@" > /dev/null 2>& 1 & 63 | echo "$!" 64 | ) 65 | exit 0 66 | else 67 | exec "${dir}/${cmd}" "$@" 68 | exit 1 69 | fi 70 | -------------------------------------------------------------------------------- /bench/chacha.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ChaCha20 = require('bcrypto/lib/chacha20'); 4 | const Poly1305 = require('bcrypto/lib/poly1305'); 5 | const hash256 = require('bcrypto/lib/hash256'); 6 | const bench = require('./bench'); 7 | 8 | console.log('note: rate measured in kb/s'); 9 | 10 | const chacha = new ChaCha20(); 11 | const poly = new Poly1305(); 12 | const key = Buffer.alloc(32, 0x02); 13 | const iv = Buffer.from('0102030405060708', 'hex'); 14 | const chunk = Buffer.allocUnsafe(32); 15 | const data = Buffer.allocUnsafe(32); 16 | 17 | for (let i = 0; i < 32; i++) 18 | chunk[i] = i; 19 | 20 | for (let i = 0; i < 32; i++) 21 | data[i] = i & 0xff; 22 | 23 | chacha.init(key, iv, 0); 24 | poly.init(key); 25 | 26 | { 27 | const end = bench('encrypt'); 28 | for (let i = 0; i < 1000000; i++) 29 | chacha.encrypt(chunk); 30 | end(1000000 * 32 / 1024); 31 | } 32 | 33 | { 34 | const end = bench('update'); 35 | for (let i = 0; i < 1000000; i++) 36 | poly.update(data); 37 | end(1000000 * 32 / 1024); 38 | } 39 | 40 | { 41 | const end = bench('finish'); 42 | for (let i = 0; i < 1000000; i++) { 43 | poly.init(key); 44 | poly.update(data); 45 | poly.finish(); 46 | } 47 | end(1000000 * 32 / 1024); 48 | } 49 | 50 | // For reference: 51 | { 52 | const end = bench('sha256'); 53 | for (let i = 0; i < 1000000; i++) 54 | hash256.digest(data); 55 | end(1000000 * 32 / 1024); 56 | } 57 | -------------------------------------------------------------------------------- /bin/spvnode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | process.title = 'bcash'; 6 | 7 | const assert = require('assert'); 8 | const SPVNode = require('../lib/node/spvnode'); 9 | const Outpoint = require('../lib/primitives/outpoint'); 10 | 11 | const node = new SPVNode({ 12 | file: true, 13 | argv: true, 14 | env: true, 15 | logFile: true, 16 | logConsole: true, 17 | logLevel: 'debug', 18 | db: 'leveldb', 19 | memory: false, 20 | persistent: true, 21 | workers: true, 22 | listen: true, 23 | loader: require 24 | }); 25 | 26 | // Temporary hack 27 | if (!node.config.bool('no-wallet') && !node.has('walletdb')) { 28 | const plugin = require('../lib/wallet/plugin'); 29 | node.use(plugin); 30 | } 31 | 32 | process.on('unhandledRejection', (err, promise) => { 33 | throw err; 34 | }); 35 | 36 | process.on('SIGINT', async () => { 37 | await node.close(); 38 | }); 39 | 40 | (async () => { 41 | await node.ensure(); 42 | await node.open(); 43 | await node.connect(); 44 | 45 | if (node.config.bool('test')) { 46 | node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8'); 47 | node.pool.watchOutpoint(new Outpoint()); 48 | node.on('block', (block) => { 49 | assert(block.txs.length >= 1); 50 | if (block.txs.length > 1) 51 | console.log(block.txs[1]); 52 | }); 53 | } 54 | 55 | node.startSync(); 56 | })().catch((err) => { 57 | console.error(err.stack); 58 | process.exit(1); 59 | }); 60 | -------------------------------------------------------------------------------- /lib/script/scripterror.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * scripterror.js - script error for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Script Error 11 | * An error thrown from the scripting system, 12 | * potentially pertaining to Script execution. 13 | * @alias module:script.ScriptError 14 | * @extends Error 15 | * @property {String} message - Error message. 16 | * @property {String} code - Original code passed in. 17 | * @property {Number} op - Opcode. 18 | * @property {Number} ip - Instruction pointer. 19 | */ 20 | 21 | class ScriptError extends Error { 22 | /** 23 | * Create an error. 24 | * @constructor 25 | * @param {String} code - Error code. 26 | * @param {Opcode} op - Opcode. 27 | * @param {Number?} ip - Instruction pointer. 28 | */ 29 | 30 | constructor(code, op, ip) { 31 | super(); 32 | 33 | this.type = 'ScriptError'; 34 | this.code = code; 35 | this.message = code; 36 | this.op = -1; 37 | this.ip = -1; 38 | 39 | if (typeof op === 'string') { 40 | this.message = op; 41 | } else if (op) { 42 | this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`; 43 | this.op = op.value; 44 | this.ip = ip; 45 | } 46 | 47 | if (Error.captureStackTrace) 48 | Error.captureStackTrace(this, ScriptError); 49 | } 50 | } 51 | 52 | /* 53 | * Expose 54 | */ 55 | 56 | module.exports = ScriptError; 57 | -------------------------------------------------------------------------------- /lib/pkg.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * pkg.js - package constants 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * Copyright (c) 2018, bcash developers. 6 | * https://github.com/bcoin-org/bcash 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const pkg = exports; 12 | 13 | /** 14 | * Package Name 15 | * @const {String} 16 | * @default 17 | */ 18 | 19 | pkg.name = require('../package.json').name; 20 | 21 | /** 22 | * Project Name 23 | * @const {String} 24 | * @default 25 | */ 26 | 27 | pkg.core = 'bcash'; 28 | 29 | /** 30 | * Organization Name 31 | * @const {String} 32 | * @default 33 | */ 34 | 35 | pkg.organization = 'bcoin-org'; 36 | 37 | /** 38 | * Currency Name 39 | * @const {String} 40 | * @default 41 | */ 42 | 43 | pkg.currency = 'bitcoin cash'; 44 | 45 | /** 46 | * Currency Unit 47 | * @const {String} 48 | * @default 49 | */ 50 | 51 | pkg.unit = 'bch'; 52 | 53 | /** 54 | * Base Unit 55 | * @const {String} 56 | * @default 57 | */ 58 | 59 | pkg.base = 'satoshi'; 60 | 61 | /** 62 | * Config file name. 63 | * @const {String} 64 | * @default 65 | */ 66 | 67 | pkg.cfg = `${pkg.core}.conf`; 68 | 69 | /** 70 | * Repository URL. 71 | * @const {String} 72 | * @default 73 | */ 74 | 75 | pkg.url = `https://github.com/${pkg.organization}/${pkg.name}`; 76 | 77 | /** 78 | * Current version string. 79 | * @const {String} 80 | */ 81 | 82 | pkg.version = require('../package.json').version; 83 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | working_directory: ~/repo 3 | docker: 4 | - image: circleci/node:10.2 5 | 6 | version: 2.0 7 | jobs: 8 | install: 9 | <<: *defaults 10 | steps: 11 | - checkout 12 | - restore_cache: 13 | keys: 14 | - v1-dependencies-{{ checksum "package.json" }} 15 | # fallback to using the latest cache if no exact match is found 16 | - v1-dependencies- 17 | - run: npm install 18 | - run: npm install eslint istanbul@1.1.0-alpha.1 codecov 19 | - save_cache: 20 | paths: 21 | - node_modules 22 | key: v1-dependencies-{{ checksum "package.json" }} 23 | - persist_to_workspace: 24 | root: . 25 | paths: . 26 | 27 | test: 28 | <<: *defaults 29 | steps: 30 | - attach_workspace: 31 | at: . 32 | - run: 33 | name: Tests with coverage 34 | command: npm run test-ci 35 | - run: 36 | name: Submit coverage 37 | command: ./node_modules/.bin/codecov 38 | 39 | lint: 40 | <<: *defaults 41 | steps: 42 | - attach_workspace: 43 | at: . 44 | - run: 45 | name: Lint 46 | command: npm run lint-ci 47 | 48 | workflows: 49 | version: 2 50 | test_and_lint: 51 | jobs: 52 | - install 53 | - lint: 54 | requires: 55 | - install 56 | - test: 57 | requires: 58 | - install 59 | -------------------------------------------------------------------------------- /bin/node: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | process.title = 'bcash'; 6 | 7 | if (process.argv.indexOf('--help') !== -1 8 | || process.argv.indexOf('-h') !== -1) { 9 | console.error('See the bcoin docs at: https://github.com/bcoin-org/bcoin/tree/master/docs.'); 10 | process.exit(1); 11 | throw new Error('Could not exit.'); 12 | } 13 | 14 | if (process.argv.indexOf('--version') !== -1 15 | || process.argv.indexOf('-v') !== -1) { 16 | const pkg = require('../package.json'); 17 | console.log(pkg.version); 18 | process.exit(0); 19 | throw new Error('Could not exit.'); 20 | } 21 | 22 | const FullNode = require('../lib/node/fullnode'); 23 | 24 | const node = new FullNode({ 25 | file: true, 26 | argv: true, 27 | env: true, 28 | logFile: true, 29 | logConsole: true, 30 | logLevel: 'debug', 31 | memory: false, 32 | workers: true, 33 | listen: true, 34 | loader: require 35 | }); 36 | 37 | // Temporary hack 38 | if (!node.config.bool('no-wallet') && !node.has('walletdb')) { 39 | const plugin = require('../lib/wallet/plugin'); 40 | node.use(plugin); 41 | } 42 | 43 | process.on('unhandledRejection', (err, promise) => { 44 | throw err; 45 | }); 46 | 47 | process.on('SIGINT', async () => { 48 | await node.close(); 49 | }); 50 | 51 | (async () => { 52 | await node.ensure(); 53 | await node.open(); 54 | await node.connect(); 55 | node.startSync(); 56 | })().catch((err) => { 57 | console.error(err.stack); 58 | process.exit(1); 59 | }); 60 | -------------------------------------------------------------------------------- /lib/workers/parent.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * parent.js - worker processes for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const EventEmitter = require('events'); 10 | 11 | /** 12 | * Parent 13 | * Represents the parent process. 14 | * @alias module:workers.Parent 15 | * @extends EventEmitter 16 | */ 17 | 18 | class Parent extends EventEmitter { 19 | /** 20 | * Create the parent process. 21 | * @constructor 22 | */ 23 | 24 | constructor() { 25 | super(); 26 | 27 | this.init(); 28 | } 29 | 30 | /** 31 | * Initialize master (node.js). 32 | * @private 33 | */ 34 | 35 | init() { 36 | process.stdin.on('data', (data) => { 37 | this.emit('data', data); 38 | }); 39 | 40 | // Nowhere to send these errors: 41 | process.stdin.on('error', () => {}); 42 | process.stdout.on('error', () => {}); 43 | process.stderr.on('error', () => {}); 44 | 45 | process.on('uncaughtException', (err) => { 46 | this.emit('exception', err); 47 | }); 48 | } 49 | 50 | /** 51 | * Send data to parent process. 52 | * @param {Buffer} data 53 | * @returns {Boolean} 54 | */ 55 | 56 | write(data) { 57 | return process.stdout.write(data); 58 | } 59 | 60 | /** 61 | * Destroy the parent process. 62 | */ 63 | 64 | destroy() { 65 | process.exit(0); 66 | } 67 | } 68 | 69 | /* 70 | * Expose 71 | */ 72 | 73 | module.exports = Parent; 74 | -------------------------------------------------------------------------------- /test/mining-template-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const assert = require('./util/assert'); 7 | const random = require('bcrypto/lib/random'); 8 | const BlockTemplate = require('../lib/mining/template'); 9 | const CoinView = require('../lib/coins/coinview'); 10 | const MTX = require('../lib/primitives/mtx'); 11 | const Address = require('../lib/primitives/address'); 12 | 13 | describe('Block Template', function () { 14 | it('should sort transactions', () => { 15 | const attempt = new BlockTemplate({}); 16 | 17 | for (let i = 0; i < 20; i++) { 18 | const tx = getRandomTX(); 19 | attempt.addTX(tx, new CoinView()); 20 | } 21 | 22 | // sort items in block template 23 | attempt.sort(); 24 | 25 | // setup coinbase 26 | attempt.refresh(); 27 | 28 | // dirty block 29 | const block = attempt.toBlock(); 30 | 31 | for (let i = 2; i < block.txs.length; i++) { 32 | const prevTX = block.txs[i - 1]; 33 | const curTX = block.txs[i]; 34 | 35 | assert(prevTX.txid() < curTX.txid(), 36 | `TX: ${prevTX.txid()} and ${curTX.txid()} are not in order.` 37 | ); 38 | } 39 | }); 40 | }); 41 | 42 | function getRandomTX() { 43 | const mtx = new MTX(); 44 | 45 | // random input 46 | mtx.addInput({ 47 | prevout: { 48 | hash: random.randomBytes(32), 49 | index: 0 50 | } 51 | }); 52 | 53 | mtx.addOutput({ 54 | address: Address.fromHash(random.randomBytes(20)), 55 | value: 0 56 | }); 57 | 58 | return mtx.toTX(); 59 | } 60 | -------------------------------------------------------------------------------- /lib/blockchain/common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * common.js - bitcoin constants for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | /** 11 | * @module blockchain/common 12 | */ 13 | 14 | /** 15 | * Locktime flags. 16 | * @enum {Number} 17 | */ 18 | 19 | exports.lockFlags = { 20 | VERIFY_SEQUENCE: 1 << 0, 21 | MEDIAN_TIME_PAST: 1 << 1 22 | }; 23 | 24 | /** 25 | * Consensus locktime flags (used for block validation). 26 | * @const {LockFlags} 27 | * @default 28 | */ 29 | 30 | exports.lockFlags.MANDATORY_LOCKTIME_FLAGS = 0; 31 | 32 | /** 33 | * Standard locktime flags (used for mempool validation). 34 | * @const {LockFlags} 35 | * @default 36 | */ 37 | 38 | exports.lockFlags.STANDARD_LOCKTIME_FLAGS = 0 39 | | exports.lockFlags.VERIFY_SEQUENCE 40 | | exports.lockFlags.MEDIAN_TIME_PAST; 41 | 42 | /** 43 | * Threshold states for versionbits 44 | * @enum {Number} 45 | * @default 46 | */ 47 | 48 | exports.thresholdStates = { 49 | DEFINED: 0, 50 | STARTED: 1, 51 | LOCKED_IN: 2, 52 | ACTIVE: 3, 53 | FAILED: 4 54 | }; 55 | 56 | /** 57 | * Verify flags for blocks. 58 | * @enum {Number} 59 | * @default 60 | */ 61 | 62 | exports.flags = { 63 | VERIFY_NONE: 0, 64 | VERIFY_POW: 1 << 0, 65 | VERIFY_BODY: 1 << 1 66 | }; 67 | 68 | /** 69 | * Default block verify flags. 70 | * @const {Number} 71 | * @default 72 | */ 73 | 74 | exports.flags.DEFAULT_FLAGS = 0 75 | | exports.flags.VERIFY_POW 76 | | exports.flags.VERIFY_BODY; 77 | -------------------------------------------------------------------------------- /lib/mining/mine.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mine.js - mining function for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const assert = require('bsert'); 10 | const hash256 = require('bcrypto/lib/hash256'); 11 | 12 | /** 13 | * Hash until the nonce overflows. 14 | * @alias module:mining.mine 15 | * @param {Buffer} data 16 | * @param {Buffer} target - Big endian. 17 | * @param {Number} min 18 | * @param {Number} max 19 | * @returns {Number} Nonce or -1. 20 | */ 21 | 22 | function mine(data, target, min, max) { 23 | let nonce = min; 24 | 25 | data.writeUInt32LE(nonce, 76, true); 26 | 27 | // The heart and soul of the miner: match the target. 28 | while (nonce <= max) { 29 | // Hash and test against the next target. 30 | if (rcmp(hash256.digest(data), target) <= 0) 31 | return nonce; 32 | 33 | // Increment the nonce to get a different hash. 34 | nonce += 1; 35 | 36 | // Update the raw buffer. 37 | data.writeUInt32LE(nonce, 76, true); 38 | } 39 | 40 | return -1; 41 | } 42 | 43 | /** 44 | * "Reverse" comparison so we don't have 45 | * to waste time reversing the block hash. 46 | * @ignore 47 | * @param {Buffer} a 48 | * @param {Buffer} b 49 | * @returns {Number} 50 | */ 51 | 52 | function rcmp(a, b) { 53 | assert(a.length === b.length); 54 | 55 | for (let i = a.length - 1; i >= 0; i--) { 56 | if (a[i] < b[i]) 57 | return -1; 58 | if (a[i] > b[i]) 59 | return 1; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | /* 66 | * Expose 67 | */ 68 | 69 | module.exports = mine; 70 | -------------------------------------------------------------------------------- /scripts/certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir=$(dirname "$(which "$0")") 4 | url='https://raw.githubusercontent.com/nodejs/node/master/src/node_root_certs.h' 5 | json=$(curl -s "$url") 6 | 7 | sha256() { 8 | cat | openssl dgst -sha256 -hex | sed 's/^(stdin)= //' 9 | } 10 | 11 | getcerts() { 12 | local buf='' 13 | echo "$json" | sed 's/\\/\\\\/g' | while read line; do 14 | if echo "$line" | grep 'BEGIN CERT' > /dev/null; then 15 | buf="$line" 16 | continue 17 | fi 18 | if echo "$line" | grep 'END CERT' > /dev/null; then 19 | buf="$buf$line" 20 | echo "$buf" | sed 's/"//g' | sed 's/,//g' 21 | continue 22 | fi 23 | buf="$buf$line" 24 | done 25 | } 26 | 27 | gethashes() { 28 | local buf='' 29 | echo "$json" | sed 's/\\n/:/g' | while read line; do 30 | if echo "$line" | grep 'BEGIN CERT' > /dev/null; then 31 | buf="$line" 32 | continue 33 | fi 34 | if echo "$line" | grep 'END CERT' > /dev/null; then 35 | buf="$buf$line" 36 | echo "$buf" \ 37 | | sed 's/"//g' \ 38 | | sed 's/,//g' \ 39 | | tr ':' '\n' \ 40 | | openssl x509 -outform DER \ 41 | | sha256 42 | continue 43 | fi 44 | buf="$buf$line" 45 | done 46 | } 47 | 48 | tojs() { 49 | local data=$(cat) 50 | local body=$(echo "$data" | head -n -1) 51 | local last=$(echo "$data" | tail -n 1) 52 | echo "'use strict';" 53 | echo '' 54 | echo 'module.exports = [' 55 | echo "$body" | while read line; do 56 | echo " '${line}'," 57 | done 58 | echo " '${last}'" 59 | echo '];' 60 | } 61 | 62 | gethashes | tojs > "${dir}/../lib/bip70/certs.js" 63 | -------------------------------------------------------------------------------- /lib/net/framer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * framer.js - packet framer for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('bsert'); 11 | const Network = require('../protocol/network'); 12 | const hash256 = require('bcrypto/lib/hash256'); 13 | 14 | /** 15 | * Protocol Message Framer 16 | * @alias module:net.Framer 17 | */ 18 | 19 | class Framer { 20 | /** 21 | * Create a framer. 22 | * @constructor 23 | * @param {Network} network 24 | */ 25 | 26 | constructor(network) { 27 | this.network = Network.get(network); 28 | } 29 | 30 | /** 31 | * Frame a payload with a header. 32 | * @param {String} cmd - Packet type. 33 | * @param {Buffer} payload 34 | * @param {Buffer?} checksum - Precomputed checksum. 35 | * @returns {Buffer} Payload with header prepended. 36 | */ 37 | 38 | packet(cmd, payload, checksum) { 39 | assert(payload, 'No payload.'); 40 | assert(cmd.length < 12); 41 | assert(payload.length <= 0xffffffff); 42 | 43 | const msg = Buffer.allocUnsafe(24 + payload.length); 44 | 45 | // Magic value 46 | msg.writeUInt32LE(this.network.magic, 0, true); 47 | 48 | // Command 49 | msg.write(cmd, 4, 'ascii'); 50 | 51 | for (let i = 4 + cmd.length; i < 16; i++) 52 | msg[i] = 0; 53 | 54 | // Payload length 55 | msg.writeUInt32LE(payload.length, 16, true); 56 | 57 | if (!checksum) 58 | checksum = hash256.digest(payload); 59 | 60 | // Checksum 61 | checksum.copy(msg, 20, 0, 4); 62 | 63 | payload.copy(msg, 24); 64 | 65 | return msg; 66 | } 67 | } 68 | 69 | /* 70 | * Expose 71 | */ 72 | 73 | module.exports = Framer; 74 | -------------------------------------------------------------------------------- /test/coin-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const Coin = require('../lib/primitives/coin'); 7 | const assert = require('./util/assert'); 8 | const common = require('../test/util/common'); 9 | 10 | const tx1 = common.readTX('tx1'); 11 | const coin1 = common.readFile('coin1.raw'); 12 | 13 | describe('Coin', function() { 14 | it('should instantiate from tx', () => { 15 | const [tx] = tx1.getTX(); 16 | const coin = Coin.fromTX(tx, 0, 0); 17 | 18 | assert.strictEqual(coin.getAddress().toBase58(), 19 | '3KUER9kZ693d5FQgvmr5qNDKnSpP9nXv9v'); 20 | assert.strictEqual(coin.getAddress().toString(), 21 | 'bitcoincash:prpswjgjta5egualmyvexu5tgzg2023rzc95lgsupf'); 22 | assert.strictEqual(coin.value, 5000000); 23 | assert.strictEqual(coin.getType(), 'multisig'); 24 | assert.strictEqual(coin.version, 1); 25 | assert.strictEqual(coin.height, 0); 26 | assert.strictEqual(coin.coinbase, false); 27 | assert.strictEqual(coin.txid(), 28 | 'ff80fe4937e2de16411c3a2bc534d661dc8b4f8aad75e6fbc4b1ec6060d9ef1c'); 29 | assert.strictEqual(coin.index, 0); 30 | }); 31 | 32 | it('should instantiate from raw', () => { 33 | const coin = Coin.fromRaw(coin1); 34 | 35 | assert.strictEqual(coin.getAddress().toBase58(), 36 | '3KUER9kZ693d5FQgvmr5qNDKnSpP9nXv9v'); 37 | assert.strictEqual(coin.getAddress().toString(), 38 | 'bitcoincash:prpswjgjta5egualmyvexu5tgzg2023rzc95lgsupf'); 39 | assert.strictEqual(coin.value, 5000000); 40 | assert.strictEqual(coin.getType(), 'multisig'); 41 | assert.strictEqual(coin.version, 1); 42 | assert.strictEqual(coin.height, 0); 43 | assert.strictEqual(coin.coinbase, false); 44 | assert.strictEqual(coin.index, 0); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/util/reorg.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('./assert'); 4 | const Chain = require('../../lib/blockchain/chain'); 5 | const CPUMiner = require('../../lib/mining/cpuminer'); 6 | 7 | /** 8 | * reorg 9 | * using miner reorgs the chain to given height 10 | * @param {Chain} chain chain 11 | * @param {CPUMiner} cpu cpuminer 12 | * @param {Number} height height 13 | * @returns {Promise} null 14 | */ 15 | async function reorg(chain, cpu, height) { 16 | assert(chain instanceof Chain); 17 | assert(cpu instanceof CPUMiner); 18 | assert(typeof height === 'number'); 19 | 20 | let tip1, tip2 = null; 21 | for (let i = 0; i < height; i++) { 22 | const job1 = await cpu.createJob(tip1); 23 | const job2 = await cpu.createJob(tip2); 24 | 25 | const blk1 = await job1.mineAsync(); 26 | const blk2 = await job2.mineAsync(); 27 | 28 | const hash1 = blk1.hash(); 29 | const hash2 = blk2.hash(); 30 | 31 | assert(await chain.add(blk1)); 32 | assert(await chain.add(blk2)); 33 | 34 | assert.bufferEqual(chain.tip.hash, hash1); 35 | 36 | tip1 = await chain.getEntry(hash1); 37 | tip2 = await chain.getEntry(hash2); 38 | 39 | assert(tip1); 40 | assert(tip2); 41 | 42 | assert(!await chain.isMainChain(tip2)); 43 | } 44 | 45 | const entry = await chain.getEntry(tip2.hash); 46 | assert(entry); 47 | assert.strictEqual(chain.height, entry.height); 48 | 49 | const block = await cpu.mineBlock(entry); 50 | assert(block); 51 | 52 | let forked = false; 53 | chain.once('reorganize', () => { 54 | forked = true; 55 | }); 56 | 57 | assert(await chain.add(block)); 58 | 59 | assert(forked); 60 | assert.bufferEqual(chain.tip.hash, block.hash()); 61 | assert(chain.tip.chainwork.gt(tip1.chainwork)); 62 | } 63 | 64 | module.exports = reorg; 65 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Welcome to the bcoin docs! 2 | 3 | ## Getting Started 4 | - [Getting Started][getting-started] 5 | - [Configuration][configuration] 6 | - [Wallet System][wallet-system] 7 | - [Design][design] 8 | - [Guides][guides] 9 | 10 | ## Running 11 | - [Bcoin CLI][cli] 12 | - [Running in the Browser][browser] 13 | - [REST and RPC API][rest-rpc] 14 | 15 | ## Code Examples 16 | - [Simple Fullnode][example-simple-fullnode] 17 | - [Connect to Peer][example-connect-peer] 18 | - [Connecting to the P2P Network][example-p2p] 19 | - [Creating a Blockchain and Mempool][example-blockchain] 20 | - [Wallet with Dummy TX][example-wallet-dummy] 21 | - [Fullnode Object][example-fullnode-wallet] 22 | - [SPV Sync][example-spv] 23 | - [Plugin Example][example-peers-plugin] 24 | - [Client API Usage][example-client-api] 25 | - [Miner with WorkerPool][example-miner-configs] 26 | - [Create and Sign TX][example-tx-create-sign] 27 | - [Get Transaction from Chain][example-tx-from-chain] 28 | 29 | 30 | [getting-started]: Beginner's-Guide.md 31 | [configuration]: Configuration.md 32 | [design]: Design.md 33 | [wallet-system]: Wallet-System.md 34 | [guides]: http://bcoin.io/guides.html 35 | 36 | [cli]: CLI.md 37 | [browser]: Running-in-the-browser.md 38 | [rest-rpc]: http://bcoin.io/api-docs/index.html#introduction 39 | 40 | [example-p2p]: Examples/connect-to-the-p2p-network.js 41 | [example-blockchain]: Examples/create-a-blockchain-and-mempool.js 42 | [example-fullnode-wallet]: Examples/fullnode-and-wallet.js 43 | [example-spv]: Examples/spv-sync-wallet.js 44 | [example-wallet-dummy]: Examples/wallet.js 45 | [example-peers-plugin]: Examples/peers-plugin.js 46 | [example-client-api]: Examples/client-api.js 47 | [example-miner-configs]: Examples/miner-configs.js 48 | [example-connect-peer]: Examples/connect-to-peer.js 49 | [example-simple-fullnode]: Examples/fullnode.js 50 | [example-tx-create-sign]: Examples/create-sign-tx.js 51 | [example-tx-from-chain]: Examples/get-tx-from-chain.js 52 | -------------------------------------------------------------------------------- /lib/workers/parent-browser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * parent.js - worker processes for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const assert = require('bsert'); 10 | const EventEmitter = require('events'); 11 | 12 | /** 13 | * Parent 14 | * Represents the parent process. 15 | * @alias module:workers.Parent 16 | * @extends EventEmitter 17 | * @ignore 18 | */ 19 | 20 | class Parent extends EventEmitter { 21 | /** 22 | * Create the parent process. 23 | * @constructor 24 | */ 25 | 26 | constructor() { 27 | super(); 28 | 29 | this.init(); 30 | } 31 | 32 | /** 33 | * Initialize master (web workers). 34 | * @private 35 | */ 36 | 37 | init() { 38 | global.onerror = (event) => { 39 | this.emit('error', new Error('Worker error.')); 40 | }; 41 | 42 | global.onmessage = (event) => { 43 | let data; 44 | if (typeof event.data === 'string') { 45 | data = Buffer.from(event.data, 'hex'); 46 | assert(data.length === event.data.length / 2); 47 | } else { 48 | assert(event.data && typeof event.data === 'object'); 49 | assert(event.data.data && typeof event.data.data.length === 'number'); 50 | data = event.data.data; 51 | data.__proto__ = Buffer.prototype; 52 | } 53 | this.emit('data', data); 54 | }; 55 | } 56 | 57 | /** 58 | * Send data to parent process. 59 | * @param {Buffer} data 60 | * @returns {Boolean} 61 | */ 62 | 63 | write(data) { 64 | if (global.postMessage.length === 2) { 65 | data.__proto__ = Uint8Array.prototype; 66 | global.postMessage({ data }, [data]); 67 | } else { 68 | global.postMessage(data.toString('hex')); 69 | } 70 | return true; 71 | } 72 | 73 | /** 74 | * Destroy the parent process. 75 | */ 76 | 77 | destroy() { 78 | global.close(); 79 | } 80 | } 81 | 82 | /* 83 | * Expose 84 | */ 85 | 86 | module.exports = Parent; 87 | -------------------------------------------------------------------------------- /lib/net/seeds/testnet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = [ 4 | '1.255.226.193:18333', 5 | '1.255.226.217:18333', 6 | '5.9.5.135:18334', 7 | '5.9.150.112:9696', 8 | '13.250.173.230:17333', 9 | '18.216.173.150:18334', 10 | '18.237.204.203:9342', 11 | '34.215.127.137:18333', 12 | '34.253.51.235:18333', 13 | '37.187.120.228:38333', 14 | '39.106.248.45:18333', 15 | '39.107.29.131:8363', 16 | '45.33.2.38:18333', 17 | '47.106.172.16:18333', 18 | '50.3.233.20:18333', 19 | '51.15.221.112:17333', 20 | '52.25.222.97:18333', 21 | '52.50.85.157:18333', 22 | '52.210.18.84:18333', 23 | '54.149.245.114:18333', 24 | '54.215.17.178:18333', 25 | '54.218.67.107:18333', 26 | '54.255.232.245:7333', 27 | '59.110.106.190:18333', 28 | '66.70.180.6:20333', 29 | '78.46.67.111:18333', 30 | '82.221.106.145:18333', 31 | '85.214.213.86:28333', 32 | '88.198.36.232:18333', 33 | '93.95.100.153:18333', 34 | '95.216.1.174:19800', 35 | '98.115.251.214:18333', 36 | '104.155.239.160:18333', 37 | '104.196.176.111:18333', 38 | '104.198.194.166:18333', 39 | '112.222.87.221:18333', 40 | '120.79.78.178:18333', 41 | '120.79.158.146:18333', 42 | '138.68.231.122:18333', 43 | '138.197.79.220:18333', 44 | '142.93.24.91:18333', 45 | '142.93.82.130:18333', 46 | '142.93.96.247:18333', 47 | '144.217.73.86:18333', 48 | '158.69.119.35:18333', 49 | '159.65.2.0:18333', 50 | '159.89.149.75:18333', 51 | '159.203.58.120:18333', 52 | '167.99.110.201:18333', 53 | '172.105.233.17:18333', 54 | '176.9.89.217:9696', 55 | '176.223.141.238:18333', 56 | '178.128.98.24:18333', 57 | '178.128.216.74:18333', 58 | '185.75.76.62:20333', 59 | '188.40.93.205:18333', 60 | '188.141.107.182:18333', 61 | '195.154.177.49:18333', 62 | '195.201.108.3:18335', 63 | '203.162.80.101:18333', 64 | '206.189.156.135:18333', 65 | '206.189.157.208:18333', 66 | '207.148.76.163:18333', 67 | '207.154.228.111:18333', 68 | '209.97.137.193:18333', 69 | '209.97.139.208:18333', 70 | '217.23.14.133:18333' 71 | ]; 72 | -------------------------------------------------------------------------------- /lib/protocol/errors.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * errors.js - error objects for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | /** 11 | * @module protocol/errors 12 | */ 13 | 14 | const assert = require('bsert'); 15 | 16 | /** 17 | * Verify Error 18 | * An error thrown during verification. Can be either 19 | * a mempool transaction validation error or a blockchain 20 | * block verification error. Ultimately used to send 21 | * `reject` packets to peers. 22 | * @extends Error 23 | * @param {Block|TX} msg 24 | * @param {String} code - Reject packet code. 25 | * @param {String} reason - Reject packet reason. 26 | * @param {Number} score - Ban score increase 27 | * (can be -1 for no reject packet). 28 | * @param {Boolean} malleated 29 | */ 30 | 31 | class VerifyError extends Error { 32 | /** 33 | * Create a verify error. 34 | * @constructor 35 | * @param {Block|TX} msg 36 | * @param {String} code - Reject packet code. 37 | * @param {String} reason - Reject packet reason. 38 | * @param {Number} score - Ban score increase 39 | * (can be -1 for no reject packet). 40 | * @param {Boolean} malleated 41 | */ 42 | 43 | constructor(msg, code, reason, score, malleated) { 44 | super(); 45 | 46 | assert(typeof code === 'string'); 47 | assert(typeof reason === 'string'); 48 | assert(score >= 0); 49 | 50 | this.type = 'VerifyError'; 51 | this.message = ''; 52 | this.code = code; 53 | this.reason = reason; 54 | this.score = score; 55 | this.hash = msg.hash(); 56 | this.malleated = malleated || false; 57 | 58 | this.message = `Verification failure: ${reason}` 59 | + ` (code=${code} score=${score} hash=${msg.rhash()})`; 60 | 61 | if (Error.captureStackTrace) 62 | Error.captureStackTrace(this, VerifyError); 63 | } 64 | } 65 | 66 | /* 67 | * Expose 68 | */ 69 | 70 | exports.VerifyError = VerifyError; 71 | -------------------------------------------------------------------------------- /docs/Examples/fullnode-and-wallet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bcoin = require('../..').set('main'); 3 | const walletPlugin = bcoin.wallet.plugin; 4 | 5 | const node = new bcoin.FullNode({ 6 | checkpoints: true, 7 | // Primary wallet passphrase 8 | passsphrase: 'node', 9 | logLevel: 'info' 10 | }); 11 | 12 | node.use(walletPlugin); 13 | 14 | // We get a lot of errors sometimes, 15 | // usually from peers hanging up on us. 16 | // Just ignore them for now. 17 | node.on('error', (err) => { 18 | ; 19 | }); 20 | 21 | // New Address we'll be sending to. 22 | const newReceiving = 'AddressHere'; 23 | 24 | // Start the node 25 | (async () => { 26 | await node.open(); 27 | 28 | const options = { 29 | id: 'mywallet', 30 | passphrase: 'foo', 31 | witness: false, 32 | type: 'pubkeyhash' 33 | }; 34 | 35 | const walletdb = node.require('walletdb').wdb; 36 | 37 | const wallet = await walletdb.create(options); 38 | 39 | console.log('Created wallet with address: %s', wallet.receiveAddress()); 40 | 41 | await node.connect(); 42 | 43 | // Start syncing the blockchain 44 | node.startSync(); 45 | 46 | // Wait for balance and send it to a new address. 47 | wallet.once('balance', async (balance) => { 48 | // Create a transaction, fill 49 | // it with coins, and sign it. 50 | const options = { 51 | subtractFee: true, 52 | outputs: [{ 53 | address: newReceiving, 54 | value: balance.total 55 | }] 56 | }; 57 | 58 | const tx = await wallet.createTX(options); 59 | const stx = await wallet.sign(tx, 'foo'); 60 | 61 | console.log('sending tx:'); 62 | console.log(stx); 63 | 64 | await node.sendTX(stx); 65 | console.log('tx sent!'); 66 | }); 67 | 68 | node.chain.on('block', (block) => { 69 | ; 70 | }); 71 | 72 | node.mempool.on('tx', (tx) => { 73 | ; 74 | }); 75 | 76 | node.chain.on('full', () => { 77 | node.mempool.getHistory().then(console.log); 78 | }); 79 | })().catch((err) => { 80 | console.error(err.stack); 81 | process.exit(1); 82 | }); 83 | -------------------------------------------------------------------------------- /test/consensus-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const assert = require('./util/assert'); 7 | const consensus = require('../lib/protocol/consensus'); 8 | const BN = require('bcrypto/lib/bn.js'); 9 | 10 | describe('Consensus', function() { 11 | it('should calculate reward properly', () => { 12 | let height = 0; 13 | let total = 0; 14 | 15 | for (;;) { 16 | const reward = consensus.getReward(height, 210000); 17 | assert(reward <= consensus.COIN * 50); 18 | total += reward; 19 | if (reward === 0) 20 | break; 21 | height++; 22 | } 23 | 24 | assert.strictEqual(height, 6930000); 25 | assert.strictEqual(total, 2099999997690000); 26 | }); 27 | 28 | it('should verify proof-of-work', () => { 29 | const bits = 0x1900896c; 30 | 31 | const hash = Buffer.from( 32 | '672b3f1bb11a994267ea4171069ba0aa4448a840f38e8f340000000000000000', 33 | 'hex' 34 | ); 35 | 36 | assert(consensus.verifyPOW(hash, bits)); 37 | }); 38 | 39 | it('should convert bits to target', () => { 40 | const bits = 0x1900896c; 41 | const target = consensus.fromCompact(bits); 42 | const expected = new BN( 43 | '0000000000000000896c00000000000000000000000000000000000000000000', 44 | 'hex'); 45 | 46 | assert.strictEqual(target.toString('hex'), expected.toString('hex')); 47 | }); 48 | 49 | it('should convert target to bits', () => { 50 | const target = new BN( 51 | '0000000000000000896c00000000000000000000000000000000000000000000', 52 | 'hex'); 53 | 54 | const bits = consensus.toCompact(target); 55 | const expected = 0x1900896c; 56 | 57 | assert.strictEqual(bits, expected); 58 | }); 59 | 60 | it('should check version bit', () => { 61 | assert(consensus.hasBit(0x20000001, 0)); 62 | assert(!consensus.hasBit(0x20000000, 0)); 63 | assert(!consensus.hasBit(0x10000001, 0)); 64 | assert(consensus.hasBit(0x20000003, 1)); 65 | assert(consensus.hasBit(0x20000003, 0)); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /etc/sample.conf: -------------------------------------------------------------------------------- 1 | # Sample bcash config file (~/.bcash/bcash.conf) 2 | 3 | # 4 | # Options 5 | # 6 | 7 | # network: main 8 | 9 | # 10 | # Node 11 | # 12 | 13 | prefix: ~/.bcash 14 | db: leveldb 15 | max-files: 64 16 | cache-size: 100 17 | 18 | # 19 | # Workers 20 | # 21 | 22 | workers: true 23 | # workers-size: 4 24 | # workers-timeout: 5000 25 | 26 | # 27 | # Logger 28 | # 29 | 30 | log-level: debug 31 | log-console: true 32 | log-file: true 33 | 34 | # 35 | # Chain 36 | # 37 | 38 | prune: false 39 | checkpoints: true 40 | coin-cache: 0 41 | entry-cache: 5000 42 | index-tx: false 43 | index-address: false 44 | 45 | # 46 | # Mempool 47 | # 48 | 49 | mempool-size: 100 50 | limit-free: true 51 | limit-free-relay: 15 52 | reject-absurd-fees: true 53 | replace-by-fee: false 54 | persistent-mempool: false 55 | 56 | # 57 | # Pool 58 | # 59 | 60 | selfish: false 61 | compact: true 62 | bip37: false 63 | listen: true 64 | max-outbound: 8 65 | max-inbound: 30 66 | 67 | # Proxy Server (browser=websockets, node=socks) 68 | # proxy: foo:bar@127.0.0.1:9050 69 | # onion: true 70 | # upnp: true 71 | 72 | # Custom list of DNS seeds 73 | # seeds: seed.bitcoin.sipa.be 74 | 75 | # Local Host & Port (to listen on) 76 | host: :: 77 | # port: 8333 78 | 79 | # Public Host & Port (to advertise to peers) 80 | # public-host: 1.2.3.4 81 | # public-port: 8444 82 | 83 | # Always try to connect to these nodes. 84 | # nodes: 127.0.0.1,127.0.0.2 85 | 86 | # Only try to connect to these nodes. 87 | # only: 127.0.0.1,127.0.0.2 88 | 89 | # 90 | # Miner 91 | # 92 | 93 | coinbase-flags: mined by bcoin 94 | # coinbase-address: 1111111111111111111114oLvT2,1111111111111111111114oLvT2 95 | preverify: false 96 | max-block-weight: 4000000 97 | reserved-block-weight: 4000 98 | reserved-block-sigops: 400 99 | 100 | # 101 | # HTTP 102 | # 103 | 104 | http-host: :: 105 | # http-port: 8332 106 | # ssl: true 107 | # ssl-cert: @/ssl/cert.crt 108 | # ssl-key: @/ssl/priv.key 109 | api-key: bikeshed 110 | # no-auth: false 111 | # cors: false 112 | -------------------------------------------------------------------------------- /docs/Examples/get-tx-from-chain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | 5 | const Logger = require('blgr'); 6 | 7 | // Setup logger to see what's Bcoin doing. 8 | const logger = new Logger({ 9 | level: 'debug' 10 | }); 11 | 12 | // Create an in-memory blockchain. 13 | const chain = new bcoin.Chain({ 14 | logger: logger, 15 | memory: true, 16 | network: 'testnet' 17 | }); 18 | 19 | const mempool = new bcoin.Mempool({ chain: chain }); 20 | 21 | // Create a network pool of peers with a limit of 8 peers. 22 | const pool = new bcoin.Pool({ 23 | logger: logger, 24 | chain: chain, 25 | mempool: mempool, 26 | maxPeers: 8 27 | }); 28 | 29 | // Create a chain indexer which indexes tx by hash 30 | const indexer = new bcoin.TXIndexer({ 31 | logger: logger, 32 | memory: true, 33 | network: 'testnet', 34 | chain: chain 35 | }); 36 | 37 | // Open the chain, pool and indexer 38 | (async function() { 39 | await logger.open(); 40 | 41 | await pool.open(); 42 | 43 | // Connect, start retrieving and relaying txs 44 | await pool.connect(); 45 | 46 | // Start the blockchain sync. 47 | pool.startSync(); 48 | 49 | await chain.open(); 50 | 51 | await indexer.open(); 52 | 53 | console.log('Current height:', chain.height); 54 | 55 | // Watch the action 56 | chain.on('block', (block) => { 57 | console.log('block: %s', block.rhash()); 58 | }); 59 | 60 | mempool.on('tx', (tx) => { 61 | console.log('tx: %s', tx.rhash); 62 | }); 63 | 64 | pool.on('tx', (tx) => { 65 | console.log('tx: %s', tx.rhash); 66 | }); 67 | 68 | await new Promise(r => setTimeout(r, 300)); 69 | 70 | await pool.stopSync(); 71 | 72 | const tip = await indexer.getTip(); 73 | const block = await chain.getBlock(tip.hash); 74 | const meta = await indexer.getMeta(block.txs[0].hash()); 75 | const tx = meta.tx; 76 | const view = await indexer.getSpentView(tx); 77 | 78 | console.log(`Tx with hash ${tx.rhash()}:`, meta); 79 | console.log(`Tx input: ${tx.getInputValue(view)},` + 80 | ` output: ${tx.getOutputValue()}, fee: ${tx.getFee(view)}`); 81 | 82 | await indexer.close(); 83 | await chain.close(); 84 | await pool.close(); 85 | })(); 86 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * One of {@link module:constants.inv}. 5 | * @typedef {Number|String} InvType 6 | * @global 7 | */ 8 | 9 | /** 10 | * An output script type. 11 | * @see {module:constants.scriptTypes} 12 | * May sometimes be a string if specified. 13 | * @typedef {Number|String} ScriptType 14 | * @global 15 | */ 16 | 17 | /** 18 | * A subset of {@link ScriptType}, including 19 | * pubkeyhash and scripthash. This value 20 | * specifically refers to the address prefix. 21 | * It is a network-agnostic way of representing 22 | * prefixes. May sometimes be a string if 23 | * specified. 24 | * @typedef {Number|String} AddressType 25 | * @global 26 | */ 27 | 28 | /** 29 | * A bitfield containing locktime flags. 30 | * @typedef {Number} LockFlags 31 | * @global 32 | */ 33 | 34 | /** 35 | * Base58 string. 36 | * @typedef {String} Base58String 37 | * @global 38 | */ 39 | 40 | /** 41 | * CashAddr string. 42 | * @typedef {String} CashAddrString 43 | * @global 44 | */ 45 | 46 | /** 47 | * Serialized address. 48 | * @typedef {Base58String|CashAddrString} AddressString 49 | * @global 50 | */ 51 | 52 | /** 53 | * Buffer or hex-string hash. 54 | * @typedef {Buffer|String} Hash 55 | * @global 56 | */ 57 | 58 | /** 59 | * Signature hash type. One of `all`, `single`, `none`, or 60 | * one of {@link constants.hashType}. 61 | * @typedef {String|Number} SighashType 62 | * @global 63 | */ 64 | 65 | /** 66 | * A satoshi amount. This is technically a 67 | * JS double float, but it is regularly 68 | * enforced to be less than 53 bits and 69 | * less than MAX_MONEY in various 70 | * functions. 71 | * @typedef {Number} Amount 72 | * @global 73 | */ 74 | 75 | /** 76 | * Rate of satoshis per kB. 77 | * @typedef {Amount} Rate 78 | * @global 79 | */ 80 | 81 | /** 82 | * A big number (bn.js) 83 | * @typedef {Object} BN 84 | * @global 85 | */ 86 | 87 | /** 88 | * A bitfield containing script verify flags. 89 | * @typedef {Number} VerifyFlags 90 | * @global 91 | */ 92 | 93 | /** 94 | * One of `main`, `testnet`, `regtest`, `segnet3`, `segnet4`. 95 | * @typedef {String} NetworkType 96 | * @see {module:network.types} 97 | * @global 98 | */ 99 | -------------------------------------------------------------------------------- /test/headers-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const Headers = require('../lib/primitives/headers'); 7 | const assert = require('./util/assert'); 8 | const common = require('./util/common'); 9 | 10 | const block1 = common.readBlock('block1'); 11 | const headers1 = common.readFile('headers1.raw'); 12 | 13 | describe('Headers', function() { 14 | it('should match headers size', () => { 15 | const headers = new Headers(); 16 | 17 | assert.strictEqual(headers.getSize(), 81); 18 | }); 19 | 20 | it('should match block1 headers from block', () => { 21 | const [blockOne] = block1.getBlock(); 22 | const headers = Headers.fromBlock(blockOne); 23 | 24 | assert.strictEqual(headers.time, 1231469665); 25 | assert.strictEqual(headers.bits, 486604799); 26 | assert.strictEqual(headers.nonce, 2573394689); 27 | assert.strictEqual(headers.version, 1); 28 | 29 | assert.strictEqual(headers.prevBlock.toString('hex'), 30 | '6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000'); 31 | assert.strictEqual(headers.merkleRoot.toString('hex'), 32 | '982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e'); 33 | assert.strictEqual(headers.rhash(), 34 | '00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048'); 35 | 36 | assert(headers.verifyBody()); 37 | assert(headers.verifyPOW()); 38 | }); 39 | 40 | it('should match block1 headers from raw', () => { 41 | const headers = Headers.fromRaw(headers1); 42 | 43 | assert.strictEqual(headers.time, 1231469665); 44 | assert.strictEqual(headers.bits, 486604799); 45 | assert.strictEqual(headers.nonce, 2573394689); 46 | assert.strictEqual(headers.version, 1); 47 | 48 | assert.strictEqual(headers.prevBlock.toString('hex'), 49 | '6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000'); 50 | assert.strictEqual(headers.merkleRoot.toString('hex'), 51 | '982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e'); 52 | assert.strictEqual(headers.rhash(), 53 | '00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048'); 54 | 55 | assert(headers.verifyBody()); 56 | assert(headers.verifyPOW()); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/mnemonic-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const assert = require('./util/assert'); 7 | const Mnemonic = require('../lib/hd/mnemonic'); 8 | const HDPrivateKey = require('../lib/hd/private'); 9 | 10 | const tests = { 11 | english: require('./data/mnemonic-english.json'), 12 | japanese: require('./data/mnemonic-japanese.json') 13 | }; 14 | 15 | describe('Mnemonic', function() { 16 | for (const language of Object.keys(tests)) { 17 | const test = tests[language]; 18 | let i = 0; 19 | 20 | for (const data of test) { 21 | const entropy = Buffer.from(data[0], 'hex'); 22 | const phrase = data[1]; 23 | const passphrase = data[2]; 24 | const seed = Buffer.from(data[3], 'hex'); 25 | const xpriv = data[4]; 26 | 27 | it(`should create a ${language} mnemonic from entropy (${i})`, () => { 28 | const mnemonic = new Mnemonic({ 29 | language, 30 | entropy 31 | }); 32 | 33 | assert.strictEqual(mnemonic.getPhrase(), phrase); 34 | assert.bufferEqual(mnemonic.getEntropy(), entropy); 35 | assert.bufferEqual(mnemonic.toSeed(passphrase), seed); 36 | 37 | const key = HDPrivateKey.fromMnemonic(mnemonic, passphrase); 38 | assert.strictEqual(key.toBase58('main'), xpriv); 39 | }); 40 | 41 | it(`should create a ${language} mnemonic from phrase (${i})`, () => { 42 | const mnemonic = new Mnemonic({ 43 | language, 44 | phrase 45 | }); 46 | 47 | assert.strictEqual(mnemonic.getPhrase(), phrase); 48 | assert.bufferEqual(mnemonic.getEntropy(), entropy); 49 | assert.bufferEqual(mnemonic.toSeed(passphrase), seed); 50 | 51 | const key = HDPrivateKey.fromMnemonic(mnemonic, passphrase); 52 | assert.strictEqual(key.toBase58('main'), xpriv); 53 | }); 54 | 55 | i += 1; 56 | } 57 | } 58 | 59 | it('should verify phrase', () => { 60 | const m1 = new Mnemonic(); 61 | const m2 = Mnemonic.fromPhrase(m1.getPhrase()); 62 | assert.bufferEqual(m2.getEntropy(), m1.getEntropy()); 63 | assert.strictEqual(m2.bits, m1.bits); 64 | assert.strictEqual(m2.language, m1.language); 65 | assert.bufferEqual(m2.toSeed(), m1.toSeed()); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/keyring-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const assert = require('./util/assert'); 7 | const KeyRing = require('../lib/primitives/keyring'); 8 | const Script = require('../lib/script/script'); 9 | 10 | const uncompressed = KeyRing.fromSecret( 11 | '5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss', 'main'); 12 | 13 | const compressed = KeyRing.fromSecret( 14 | 'L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1', 'main'); 15 | 16 | describe('KeyRing', function() { 17 | it('should get uncompressed public key', () => { 18 | assert.strictEqual( 19 | '04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b' 20 | + '8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235', 21 | uncompressed.getPublicKey('hex')); 22 | }); 23 | 24 | it('should get uncompressed public key address', () => { 25 | assert.strictEqual( 26 | '1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN', 27 | uncompressed.getKeyAddress('base58', 'main')); 28 | }); 29 | 30 | it('should get uncompressed WIF', () => { 31 | assert.strictEqual( 32 | '5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss', 33 | uncompressed.toSecret('main')); 34 | }); 35 | 36 | it('should get compressed public key', () => { 37 | assert.strictEqual( 38 | '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd', 39 | compressed.getPublicKey('hex')); 40 | }); 41 | 42 | it('should get compressed public key address', () => { 43 | assert.strictEqual( 44 | '1F3sAm6ZtwLAUnj7d38pGFxtP3RVEvtsbV', 45 | compressed.getKeyAddress('base58', 'main')); 46 | }); 47 | 48 | it('should get compressed WIF', () => { 49 | assert.strictEqual( 50 | 'L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1', 51 | compressed.toSecret('main')); 52 | }); 53 | 54 | it('should get keys from multisig', () => { 55 | const script = Script.fromMultisig(1, 2, [ 56 | compressed.getPublicKey(), 57 | uncompressed.getPublicKey()]); 58 | 59 | assert.strictEqual( 60 | compressed.getPublicKey(), 61 | KeyRing.fromMultisigScript(script, 1).getPublicKey()); 62 | assert.strictEqual( 63 | uncompressed.getPublicKey(), 64 | KeyRing.fromMultisigScript(script, 2).getPublicKey()); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /docs/Examples/create-sign-tx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint new-cap: "off" */ 4 | 5 | const bcoin = require('../..'); 6 | const assert = require('assert'); 7 | 8 | (async () => { 9 | const master = bcoin.hd.generate(); 10 | const key = master.derivePath('m/44/0/0/0/0'); 11 | const keyring = new bcoin.wallet.WalletKey(key.privateKey); 12 | const cb = new bcoin.MTX(); 13 | 14 | cb.addInput({ 15 | prevout: new bcoin.Outpoint(), 16 | script: new bcoin.Script(), 17 | sequence: 0xffffffff 18 | }); 19 | 20 | // Send 50,000 satoshis to ourselves. 21 | cb.addOutput({ 22 | address: keyring.getAddress(), 23 | value: 50000 24 | }); 25 | 26 | // Our available coins. 27 | const coins = []; 28 | 29 | // Convert the coinbase output to a Coin 30 | // object and add it to our available coins. 31 | // In reality you might get these coins from a wallet. 32 | const coin = bcoin.Coin.fromTX(cb, 0, -1); 33 | coins.push(coin); 34 | 35 | // Create our redeeming transaction. 36 | const mtx = new bcoin.MTX(); 37 | 38 | // Send 10,000 satoshis to ourself. 39 | mtx.addOutput({ 40 | address: keyring.getAddress(), 41 | value: 10000 42 | }); 43 | 44 | // Now that we've created the output, we can do some coin selection (the 45 | // output must be added first so we know how much money is needed and also so 46 | // we can accurately estimate the size for fee calculation). 47 | 48 | // Select coins from our array and add inputs. 49 | // Calculate fee and add a change output. 50 | await mtx.fund(coins, { 51 | // Use a rate of 10,000 satoshis per kb. 52 | // With the `fullnode` object, you can 53 | // use the fee estimator for this instead 54 | // of blindly guessing. 55 | rate: 10000, 56 | // Send the change back to ourselves. 57 | changeAddress: keyring.getAddress() 58 | }); 59 | 60 | // Sign input 0 61 | mtx.sign(keyring); 62 | 63 | // The transaction should now verify. 64 | assert(mtx.verify()); 65 | 66 | // Commit our transaction and make it immutable. 67 | // This turns it from an MTX into a TX. 68 | const tx = mtx.toTX(); 69 | 70 | // The transaction should still verify. 71 | // Regular transactions require a coin 72 | // viewpoint to be passed in. 73 | assert(tx.verify(mtx.view)); 74 | 75 | console.log(mtx); 76 | })().catch((err) => { 77 | console.error(err.stack); 78 | process.exit(1); 79 | }); 80 | -------------------------------------------------------------------------------- /lib/utils/binary.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * binary.js - binary search utils for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | /** 11 | * Perform a binary search on a sorted array. 12 | * @param {Array} items 13 | * @param {Object} key 14 | * @param {Function} compare 15 | * @param {Boolean?} insert 16 | * @returns {Number} Index. 17 | */ 18 | 19 | exports.search = function search(items, key, compare, insert) { 20 | let start = 0; 21 | let end = items.length - 1; 22 | 23 | while (start <= end) { 24 | const pos = (start + end) >>> 1; 25 | const cmp = compare(items[pos], key); 26 | 27 | if (cmp === 0) 28 | return pos; 29 | 30 | if (cmp < 0) 31 | start = pos + 1; 32 | else 33 | end = pos - 1; 34 | } 35 | 36 | if (!insert) 37 | return -1; 38 | 39 | return start; 40 | }; 41 | 42 | /** 43 | * Perform a binary insert on a sorted array. 44 | * @param {Array} items 45 | * @param {Object} item 46 | * @param {Function} compare 47 | * @returns {Number} index 48 | */ 49 | 50 | exports.insert = function insert(items, item, compare, uniq) { 51 | const i = exports.search(items, item, compare, true); 52 | 53 | if (uniq && i < items.length) { 54 | if (compare(items[i], item) === 0) 55 | return -1; 56 | } 57 | 58 | if (i === 0) 59 | items.unshift(item); 60 | else if (i === items.length) 61 | items.push(item); 62 | else 63 | items.splice(i, 0, item); 64 | 65 | return i; 66 | }; 67 | 68 | /** 69 | * Perform a binary removal on a sorted array. 70 | * @param {Array} items 71 | * @param {Object} item 72 | * @param {Function} compare 73 | * @returns {Boolean} 74 | */ 75 | 76 | exports.remove = function remove(items, item, compare) { 77 | const i = exports.search(items, item, compare, false); 78 | 79 | if (i === -1) 80 | return false; 81 | 82 | splice(items, i); 83 | 84 | return true; 85 | }; 86 | 87 | /* 88 | * Helpers 89 | */ 90 | 91 | function splice(list, i) { 92 | if (i === 0) { 93 | list.shift(); 94 | return; 95 | } 96 | 97 | let k = i + 1; 98 | 99 | while (k < list.length) 100 | list[i++] = list[k++]; 101 | 102 | list.pop(); 103 | } 104 | -------------------------------------------------------------------------------- /lib/workers/child-browser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * child.js - child processes for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | /* global register */ 8 | 9 | 'use strict'; 10 | 11 | const assert = require('bsert'); 12 | const EventEmitter = require('events'); 13 | 14 | /** 15 | * Child 16 | * Represents a child process. 17 | * @alias module:workers.Child 18 | * @extends EventEmitter 19 | * @ignore 20 | */ 21 | 22 | class Child extends EventEmitter { 23 | /** 24 | * Represents a child process. 25 | * @constructor 26 | * @param {String} file 27 | */ 28 | 29 | constructor(file) { 30 | super(); 31 | 32 | this.init(file); 33 | } 34 | 35 | /** 36 | * Test whether child process support is available. 37 | * @returns {Boolean} 38 | */ 39 | 40 | static hasSupport() { 41 | return typeof global.postMessage === 'function'; 42 | } 43 | 44 | /** 45 | * Initialize child process. Bind to events. 46 | * @private 47 | * @param {String} file 48 | */ 49 | 50 | init(file) { 51 | if (process.env.BMOCHA) 52 | register(file, [__dirname, file]); 53 | 54 | this.child = new global.Worker(file); 55 | 56 | this.child.onerror = (event) => { 57 | this.emit('error', new Error('Child error.')); 58 | this.emit('exit', 1, null); 59 | }; 60 | 61 | this.child.onmessage = (event) => { 62 | let data; 63 | if (typeof event.data === 'string') { 64 | data = Buffer.from(event.data, 'hex'); 65 | assert(data.length === event.data.length / 2); 66 | } else { 67 | assert(event.data && typeof event.data === 'object'); 68 | assert(event.data.data && typeof event.data.data.length === 'number'); 69 | data = event.data.data; 70 | data.__proto__ = Buffer.prototype; 71 | } 72 | this.emit('data', data); 73 | }; 74 | } 75 | 76 | /** 77 | * Send data to child process. 78 | * @param {Buffer} data 79 | * @returns {Boolean} 80 | */ 81 | 82 | write(data) { 83 | if (this.child.postMessage.length === 2) { 84 | data.__proto__ = Uint8Array.prototype; 85 | this.child.postMessage({ data }, [data]); 86 | } else { 87 | this.child.postMessage(data.toString('hex')); 88 | } 89 | return true; 90 | } 91 | 92 | /** 93 | * Destroy the child process. 94 | */ 95 | 96 | destroy() { 97 | this.child.terminate(); 98 | this.emit('exit', 15 | 0x80, 'SIGTERM'); 99 | } 100 | } 101 | 102 | /* 103 | * Expose 104 | */ 105 | 106 | module.exports = Child; 107 | -------------------------------------------------------------------------------- /docs/Examples/connect-to-the-p2p-network.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bcoin = require('../..').set('main'); 3 | 4 | const Logger = require('blgr'); 5 | 6 | // Setup logger to see what's Bcoin doing. 7 | const logger = new Logger({ 8 | level: 'debug' 9 | }); 10 | 11 | // Create a blockchain and store it in memory. 12 | const chain = new bcoin.Chain({ 13 | memory: true, 14 | network: 'main', 15 | logger: logger 16 | }); 17 | 18 | const mempool = new bcoin.Mempool({ 19 | chain: chain, 20 | logger: logger 21 | }); 22 | 23 | // Create a network pool of peers with a limit of 8 peers. 24 | const pool = new bcoin.Pool({ 25 | chain: chain, 26 | mempool: mempool, 27 | maxPeers: 8, 28 | logger: logger 29 | }); 30 | 31 | (async function() { 32 | await logger.open(); 33 | await chain.open(); 34 | 35 | await pool.open(); 36 | 37 | // Connect, start retrieving and relaying txs 38 | await pool.connect(); 39 | 40 | // Start the blockchain sync. 41 | pool.startSync(); 42 | 43 | // Watch the action 44 | chain.on('block', (block) => { 45 | console.log('Connected block to blockchain:'); 46 | console.log(block); 47 | }); 48 | 49 | mempool.on('tx', (tx) => { 50 | console.log('Added tx to mempool:'); 51 | console.log(tx); 52 | }); 53 | 54 | pool.on('tx', (tx) => { 55 | console.log('Saw transaction:'); 56 | console.log(tx.rhash); 57 | }); 58 | })(); 59 | 60 | // Start up a testnet sync in-memory 61 | // while we're at it (because we can). 62 | 63 | const tchain = new bcoin.Chain({ 64 | memory: true, 65 | network: 'testnet', 66 | logger: logger 67 | }); 68 | 69 | const tmempool = new bcoin.Mempool({ 70 | network: 'testnet', 71 | chain: tchain, 72 | logger: logger 73 | }); 74 | 75 | const tpool = new bcoin.Pool({ 76 | network: 'testnet', 77 | chain: tchain, 78 | mempool: tmempool, 79 | size: 8, 80 | logger: logger 81 | }); 82 | 83 | (async function() { 84 | await tchain.open(); 85 | 86 | await tpool.open(); 87 | 88 | // Connect, start retrieving and relaying txs 89 | await tpool.connect(); 90 | 91 | // Start the blockchain sync. 92 | tpool.startSync(); 93 | 94 | tchain.on('block', (block) => { 95 | console.log('Added testnet block:'); 96 | console.log(block); 97 | }); 98 | 99 | tmempool.on('tx', (tx) => { 100 | console.log('Added testnet tx to mempool:'); 101 | console.log(tx); 102 | }); 103 | 104 | tpool.on('tx', (tx) => { 105 | console.log('Saw testnet transaction:'); 106 | console.log(tx); 107 | }); 108 | })().catch((err) => { 109 | console.error(err.stack); 110 | process.exit(1); 111 | }); 112 | -------------------------------------------------------------------------------- /lib/wallet/client.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * client.js - http client for wallets 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('bsert'); 11 | const {NodeClient} = require('bclient'); 12 | const util = require('../utils/util'); 13 | const TX = require('../primitives/tx'); 14 | const hash256 = require('bcrypto/lib/hash256'); 15 | 16 | const parsers = { 17 | 'block connect': (entry, txs) => parseBlock(entry, txs), 18 | 'block disconnect': entry => [parseEntry(entry)], 19 | 'block rescan': (entry, txs) => parseBlock(entry, txs), 20 | 'chain reset': entry => [parseEntry(entry)], 21 | 'tx': tx => [TX.fromRaw(tx)] 22 | }; 23 | 24 | class WalletClient extends NodeClient { 25 | constructor(options) { 26 | super(options); 27 | } 28 | 29 | bind(event, handler) { 30 | const parser = parsers[event]; 31 | 32 | if (!parser) { 33 | super.bind(event, handler); 34 | return; 35 | } 36 | 37 | super.bind(event, (...args) => { 38 | return handler(...parser(...args)); 39 | }); 40 | } 41 | 42 | hook(event, handler) { 43 | const parser = parsers[event]; 44 | 45 | if (!parser) { 46 | super.hook(event, handler); 47 | return; 48 | } 49 | 50 | super.hook(event, (...args) => { 51 | return handler(...parser(...args)); 52 | }); 53 | } 54 | 55 | async getTip() { 56 | return parseEntry(await super.getTip()); 57 | } 58 | 59 | async getEntry(block) { 60 | if (Buffer.isBuffer(block)) 61 | block = util.revHex(block); 62 | 63 | return parseEntry(await super.getEntry(block)); 64 | } 65 | 66 | async send(tx) { 67 | return super.send(tx.toRaw()); 68 | } 69 | 70 | async setFilter(filter) { 71 | return super.setFilter(filter.toRaw()); 72 | } 73 | 74 | async rescan(start) { 75 | if (Buffer.isBuffer(start)) 76 | start = util.revHex(start); 77 | 78 | return super.rescan(start); 79 | } 80 | } 81 | 82 | /* 83 | * Helpers 84 | */ 85 | 86 | function parseEntry(data) { 87 | assert(Buffer.isBuffer(data)); 88 | assert(data.length >= 84); 89 | 90 | const hash = hash256.digest(data.slice(0, 80)); 91 | 92 | return { 93 | hash: hash, 94 | height: data.readUInt32LE(80, true), 95 | time: data.readUInt32LE(68, true) 96 | }; 97 | } 98 | 99 | function parseBlock(entry, txs) { 100 | const block = parseEntry(entry); 101 | const out = []; 102 | 103 | for (const tx of txs) 104 | out.push(TX.fromRaw(tx)); 105 | 106 | return [block, out]; 107 | } 108 | 109 | /* 110 | * Expose 111 | */ 112 | 113 | module.exports = WalletClient; 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## :warning: IMPORTANT: This repository is not currently being actively maintained. 2 | #### It has not been updated for compatibility with the [May 15 2019 Bitcoin Cash hard fork.](https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-05-15-upgrade.md) Although SPV mode and other non-full-consensus modules in this library may still operate correctly, use at your own risk. Forks of this repository may exist that have been adapted to the latest network upgrade, but those changes will not have been reviewed by bcoin organization members. 3 | 4 | 5 | # Bcash 6 | 7 | [![CircleCi Status][circleci-status-img]][circleci-status-url] 8 | [![Coverage Status][coverage-status-img]][coverage-status-url] 9 | 10 | **Bcash** is an alternative implementation of the bitcoin cash protocol, 11 | written in node.js. 12 | 13 | Bcash is undergoing development and testing and is in alpha stage. Bcash 14 | is a fork of [bcoin][bcoin] and has the same RPC API. 15 | 16 | ## Uses 17 | 18 | - Full Node 19 | - SPV Node 20 | - Wallet Backend (bip44 derivation) 21 | - Mining Backend (getblocktemplate support) 22 | - General Purpose Bitcoin Library 23 | 24 | Try it in the browser: https://bcoin.io/browser/ 25 | 26 | ## Install 27 | 28 | ``` 29 | $ git clone git://github.com/bcoin-org/bcash.git 30 | $ cd bcash 31 | $ npm install 32 | $ ./bin/bcash 33 | ``` 34 | 35 | See the [Beginner's Guide][guide] for more in-depth installation instructions. 36 | 37 | ## Documentation 38 | 39 | - API Docs: https://bcoin.io/docs/ 40 | - REST Docs: https://bcoin.io/api-docs/ 41 | - Docs: [docs/](docs/README.md) 42 | 43 | ## Support 44 | 45 | Join us on [freenode][freenode] in the [#bcoin][irc] channel. 46 | 47 | ## Disclaimer 48 | 49 | Bcash does not guarantee you against theft or lost funds due to bugs, mishaps, 50 | or your own incompetence. You and you alone are responsible for securing your 51 | money. 52 | 53 | ## Contribution and License Agreement 54 | 55 | If you contribute code to this project, you are implicitly allowing your code 56 | to be distributed under the MIT license. You are also implicitly verifying that 57 | all code is your original work. `` 58 | 59 | ## License 60 | 61 | - Copyright (c) 2014-2015, Fedor Indutny (MIT License). 62 | - Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 63 | - Copyright (c) 2018, bcash developers. 64 | 65 | See LICENSE for more info. 66 | 67 | [bcoin]: https://bcoin.io 68 | [purse]: https://purse.io 69 | [freenode]: https://freenode.net/ 70 | [irc]: irc://irc.freenode.net/bcoin 71 | [guide]: ./docs/Beginner's-Guide.md 72 | [changelog]: ./CHANGELOG.md 73 | 74 | 75 | [coverage-status-img]: https://codecov.io/gh/bcoin-org/bcash/badge.svg?branch=master 76 | [coverage-status-url]: https://codecov.io/gh/bcoin-org/bcash?branch=master 77 | [circleci-status-img]: https://circleci.com/gh/bcoin-org/bcash/tree/master.svg?style=shield 78 | [circleci-status-url]: https://circleci.com/gh/bcoin-org/bcash/tree/master 79 | -------------------------------------------------------------------------------- /lib/utils/util.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * util.js - utils for bcoin 3 | * Copyright (c) 2014-2015, Fedor Indutny (MIT License) 4 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 5 | * https://github.com/bcoin-org/bcoin 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('bsert'); 11 | 12 | /** 13 | * @exports utils/util 14 | */ 15 | 16 | const util = exports; 17 | 18 | /** 19 | * Return hrtime (shim for browser). 20 | * @param {Array} time 21 | * @returns {Array} [seconds, nanoseconds] 22 | */ 23 | 24 | util.bench = function bench(time) { 25 | if (!process.hrtime) { 26 | const now = Date.now(); 27 | 28 | if (time) { 29 | const [hi, lo] = time; 30 | const start = hi * 1000 + lo / 1e6; 31 | return now - start; 32 | } 33 | 34 | const ms = now % 1000; 35 | 36 | // Seconds 37 | const hi = (now - ms) / 1000; 38 | 39 | // Nanoseconds 40 | const lo = ms * 1e6; 41 | 42 | return [hi, lo]; 43 | } 44 | 45 | if (time) { 46 | const [hi, lo] = process.hrtime(time); 47 | return hi * 1000 + lo / 1e6; 48 | } 49 | 50 | return process.hrtime(); 51 | }; 52 | 53 | /** 54 | * Get current time in unix time (seconds). 55 | * @returns {Number} 56 | */ 57 | 58 | util.now = function now() { 59 | return Math.floor(Date.now() / 1000); 60 | }; 61 | 62 | /** 63 | * Get current time in unix time (milliseconds). 64 | * @returns {Number} 65 | */ 66 | 67 | util.ms = function ms() { 68 | return Date.now(); 69 | }; 70 | 71 | /** 72 | * Create a Date ISO string from time in unix time (seconds). 73 | * @param {Number?} time - Seconds in unix time. 74 | * @returns {String} 75 | */ 76 | 77 | util.date = function date(time) { 78 | if (time == null) 79 | time = util.now(); 80 | 81 | return new Date(time * 1000).toISOString().slice(0, -5) + 'Z'; 82 | }; 83 | 84 | /** 85 | * Get unix seconds from a Date string. 86 | * @param {String?} date - Date ISO String. 87 | * @returns {Number} 88 | */ 89 | 90 | util.time = function time(date) { 91 | if (date == null) 92 | return util.now(); 93 | 94 | return new Date(date) / 1000 | 0; 95 | }; 96 | 97 | /** 98 | * Reverse a hex-string. 99 | * @param {String} str - Hex string. 100 | * @returns {String} Reversed hex string. 101 | */ 102 | 103 | util.revHex = function revHex(buf) { 104 | assert(Buffer.isBuffer(buf)); 105 | 106 | const str = buf.toString('hex'); 107 | 108 | let out = ''; 109 | 110 | for (let i = str.length - 2; i >= 0; i -= 2) 111 | out += str[i] + str[i + 1]; 112 | 113 | return out; 114 | }; 115 | 116 | util.fromRev = function fromRev(str) { 117 | assert(typeof str === 'string'); 118 | assert((str.length & 1) === 0); 119 | 120 | let out = ''; 121 | 122 | for (let i = str.length - 2; i >= 0; i -= 2) 123 | out += str[i] + str[i + 1]; 124 | 125 | return Buffer.from(out, 'hex'); 126 | }; 127 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 9 9 | }, 10 | "root": true, 11 | "rules": { 12 | "array-bracket-spacing": ["error", "never"], 13 | "arrow-parens": ["error", "as-needed", { 14 | "requireForBlockBody": true 15 | }], 16 | "arrow-spacing": "error", 17 | "block-spacing": ["error", "always"], 18 | "brace-style": ["error", "1tbs"], 19 | "camelcase": ["error", { 20 | "properties": "never" 21 | }], 22 | "comma-dangle": ["error", "never"], 23 | "consistent-return": "error", 24 | "eol-last": ["error", "always"], 25 | "eqeqeq": ["error", "always", { 26 | "null": "ignore" 27 | }], 28 | "func-name-matching": "error", 29 | "indent": ["off", 2, { 30 | "SwitchCase": 1, 31 | "CallExpression": { 32 | "arguments": "off" 33 | }, 34 | "ArrayExpression": "off" 35 | }], 36 | "handle-callback-err": "off", 37 | "linebreak-style": ["error", "unix"], 38 | "max-len": ["error", { 39 | "code": 80, 40 | "ignorePattern": "function \\w+\\(", 41 | "ignoreUrls": true 42 | }], 43 | "max-statements-per-line": ["error", { 44 | "max": 1 45 | }], 46 | "new-cap": ["error", { 47 | "newIsCap": true, 48 | "capIsNew": false 49 | }], 50 | "new-parens": "error", 51 | "no-buffer-constructor": "error", 52 | "no-console": "off", 53 | "no-extra-semi": "off", 54 | "no-fallthrough": "off", 55 | "no-func-assign": "off", 56 | "no-implicit-coercion": "error", 57 | "no-multi-assign": "error", 58 | "no-multiple-empty-lines": ["error", { 59 | "max": 1 60 | }], 61 | "no-nested-ternary": "error", 62 | "no-param-reassign": "off", 63 | "no-return-assign": "error", 64 | "no-return-await": "off", 65 | "no-shadow-restricted-names": "error", 66 | "no-tabs": "error", 67 | "no-trailing-spaces": "error", 68 | "no-unused-vars": ["error", { 69 | "vars": "all", 70 | "args": "none", 71 | "ignoreRestSiblings": false 72 | }], 73 | "no-use-before-define": ["error", { 74 | "functions": false, 75 | "classes": false 76 | }], 77 | "no-useless-escape": "off", 78 | "no-var": "error", 79 | "nonblock-statement-body-position": ["error", "below"], 80 | "padded-blocks": ["error", "never"], 81 | "prefer-arrow-callback": "error", 82 | "prefer-const": ["error", { 83 | "destructuring": "all", 84 | "ignoreReadBeforeAssign": true 85 | }], 86 | "prefer-template": "off", 87 | "quotes": ["error", "single"], 88 | "semi": ["error", "always"], 89 | "spaced-comment": ["error", "always", { 90 | "exceptions": ["!"] 91 | }], 92 | "space-before-blocks": "error", 93 | "strict": "error", 94 | "unicode-bom": ["error", "never"], 95 | "valid-jsdoc": "error", 96 | "wrap-iife": ["error", "inside"] 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/wallet/layout.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * layout.js - data layout for wallets 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const bdb = require('bdb'); 10 | 11 | /* 12 | * Wallet Database Layout: 13 | * V -> db version 14 | * O -> flags 15 | * R -> chain sync state 16 | * D -> wallet id depth 17 | * p[addr-hash] -> wallet ids 18 | * P[wid][addr-hash] -> path data 19 | * r[wid][index][hash] -> path account index 20 | * w[wid] -> wallet 21 | * W[wid] -> wallet id 22 | * l[id] -> wid 23 | * a[wid][index] -> account 24 | * i[wid][name] -> account index 25 | * n[wid][index] -> account name 26 | * h[height] -> recent block hash 27 | * b[height] -> block->wid map 28 | * o[hash][index] -> outpoint->wid map 29 | * T[hash] -> tx->wid map 30 | * t[wid]* -> txdb 31 | */ 32 | 33 | exports.wdb = { 34 | V: bdb.key('V'), 35 | O: bdb.key('O'), 36 | R: bdb.key('R'), 37 | D: bdb.key('D'), 38 | p: bdb.key('p', ['hash']), 39 | P: bdb.key('P', ['uint32', 'hash']), 40 | r: bdb.key('r', ['uint32', 'uint32', 'hash']), 41 | w: bdb.key('w', ['uint32']), 42 | W: bdb.key('W', ['uint32']), 43 | l: bdb.key('l', ['ascii']), 44 | a: bdb.key('a', ['uint32', 'uint32']), 45 | i: bdb.key('i', ['uint32', 'ascii']), 46 | n: bdb.key('n', ['uint32', 'uint32']), 47 | h: bdb.key('h', ['uint32']), 48 | b: bdb.key('b', ['uint32']), 49 | o: bdb.key('o', ['hash256', 'uint32']), 50 | T: bdb.key('T', ['hash256']), 51 | t: bdb.key('t', ['uint32']) 52 | }; 53 | 54 | /* 55 | * TXDB Database Layout: 56 | * R -> wallet balance 57 | * r[account] -> account balance 58 | * t[hash] -> extended tx 59 | * c[hash][index] -> coin 60 | * d[hash][index] -> undo coin 61 | * s[hash][index] -> spent by hash 62 | * p[hash] -> dummy (pending flag) 63 | * m[time][hash] -> dummy (tx by time) 64 | * h[height][hash] -> dummy (tx by height) 65 | * T[account][hash] -> dummy (tx by account) 66 | * P[account][hash] -> dummy (pending tx by account) 67 | * M[account][time][hash] -> dummy (tx by time + account) 68 | * H[account][height][hash] -> dummy (tx by height + account) 69 | * C[account][hash][index] -> dummy (coin by account) 70 | * b[height] -> block record 71 | */ 72 | 73 | exports.txdb = { 74 | prefix: bdb.key('t', ['uint32']), 75 | R: bdb.key('R'), 76 | r: bdb.key('r', ['uint32']), 77 | t: bdb.key('t', ['hash256']), 78 | c: bdb.key('c', ['hash256', 'uint32']), 79 | d: bdb.key('d', ['hash256', 'uint32']), 80 | s: bdb.key('s', ['hash256', 'uint32']), 81 | p: bdb.key('p', ['hash256']), 82 | m: bdb.key('m', ['uint32', 'hash256']), 83 | h: bdb.key('h', ['uint32', 'hash256']), 84 | T: bdb.key('T', ['uint32', 'hash256']), 85 | P: bdb.key('P', ['uint32', 'hash256']), 86 | M: bdb.key('M', ['uint32', 'uint32', 'hash256']), 87 | H: bdb.key('H', ['uint32', 'uint32', 'hash256']), 88 | C: bdb.key('C', ['uint32', 'hash256', 'uint32']), 89 | b: bdb.key('b', ['uint32']) 90 | }; 91 | -------------------------------------------------------------------------------- /docs/Examples/client-api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcoin = require('../..'); 4 | const client = require('bclient'); 5 | const plugin = bcoin.wallet.plugin; 6 | const network = bcoin.Network.get('regtest'); 7 | 8 | const node = new bcoin.FullNode({ 9 | network: 'regtest', 10 | apiKey: 'foo', 11 | walletAuth: true, 12 | db: 'memory' 13 | }); 14 | 15 | node.use(plugin); 16 | 17 | const wallet = new client.WalletClient({ 18 | port: network.walletPort, 19 | apiKey: 'foo' 20 | }); 21 | 22 | async function fundWallet(wdb, addr) { 23 | // Coinbase 24 | const mtx = new bcoin.MTX(); 25 | mtx.addOutpoint(new bcoin.Outpoint()); 26 | mtx.addOutput(addr, 50460); 27 | mtx.addOutput(addr, 50460); 28 | mtx.addOutput(addr, 50460); 29 | mtx.addOutput(addr, 50460); 30 | 31 | const tx = mtx.toTX(); 32 | 33 | wallet.once('balance', (balance) => { 34 | console.log('New Balance:'); 35 | console.log(balance); 36 | }); 37 | 38 | wallet.once('address', (receive) => { 39 | console.log('New Receiving Address:'); 40 | console.log(receive); 41 | }); 42 | 43 | wallet.once('tx', (details) => { 44 | console.log('New Wallet TX:'); 45 | console.log(details); 46 | }); 47 | 48 | await wdb.addTX(tx); 49 | await new Promise(r => setTimeout(r, 300)); 50 | } 51 | 52 | async function sendTX(addr, value) { 53 | const options = { 54 | rate: 10000, 55 | outputs: [{ 56 | value: value, 57 | address: addr 58 | }] 59 | }; 60 | 61 | const tx = await wallet.send('test', options); 62 | 63 | return tx.hash; 64 | } 65 | 66 | async function callNodeApi() { 67 | const info = await wallet.client.getInfo(); 68 | 69 | console.log('Server Info:'); 70 | console.log(info); 71 | 72 | const json = await wallet.client.rpc.execute('getblocktemplate', []); 73 | 74 | console.log('Block Template (RPC):'); 75 | console.log(json); 76 | } 77 | 78 | (async () => { 79 | const wdb = node.require('walletdb').wdb; 80 | 81 | await node.open(); 82 | 83 | const w = await wallet.createWallet('test'); 84 | 85 | console.log('Wallet:'); 86 | console.log(w); 87 | 88 | // Fund default account. 89 | const receive = await wallet.createAddress('test', 'default'); 90 | await fundWallet(wdb, receive.address); 91 | 92 | const balance = await wallet.getBalance('test', 'default'); 93 | 94 | console.log('Balance:'); 95 | console.log(balance); 96 | 97 | const acct = await wallet.createAccount('test', 'foo'); 98 | 99 | console.log('Account:'); 100 | console.log(acct); 101 | 102 | // Send to our new account. 103 | const hash = await sendTX(acct.receiveAddress, 10000); 104 | 105 | console.log('Sent TX:'); 106 | console.log(hash); 107 | 108 | const tx = await wallet.getTX(hash); 109 | 110 | console.log('Sent TX details:'); 111 | console.log(tx); 112 | 113 | await callNodeApi(); 114 | })().catch((err) => { 115 | console.error(err.stack); 116 | process.exit(1); 117 | }); 118 | -------------------------------------------------------------------------------- /test/util/node-context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const FullNode = require('../../lib/node/fullnode'); 5 | const Network = require('../../lib/protocol/network'); 6 | const Logger = require('blgr'); 7 | 8 | class NodeContext { 9 | constructor(network, size) { 10 | this.network = Network.get(network); 11 | this.size = size || 4; 12 | this.nodes = []; 13 | 14 | this.init(); 15 | } 16 | 17 | init() { 18 | for (let i = 0; i < this.size; i++) { 19 | const port = this.network.port + i; 20 | 21 | let last = port - 1; 22 | 23 | if (last < this.network.port) 24 | last = port; 25 | 26 | const node = new FullNode({ 27 | network: this.network, 28 | memory: true, 29 | logger: new Logger({ 30 | level: 'debug', 31 | file: false, 32 | console: false 33 | }), 34 | listen: true, 35 | publicHost: '127.0.0.1', 36 | publicPort: port, 37 | httpPort: port + 100, 38 | host: '127.0.0.1', 39 | port: port, 40 | seeds: [ 41 | `127.0.0.1:${last}` 42 | ] 43 | }); 44 | 45 | node.on('error', (err) => { 46 | node.logger.error(err); 47 | }); 48 | 49 | this.nodes.push(node); 50 | } 51 | } 52 | 53 | open() { 54 | const jobs = []; 55 | 56 | for (const node of this.nodes) 57 | jobs.push(node.open()); 58 | 59 | return Promise.all(jobs); 60 | } 61 | 62 | close() { 63 | const jobs = []; 64 | 65 | for (const node of this.nodes) 66 | jobs.push(node.close()); 67 | 68 | return Promise.all(jobs); 69 | } 70 | 71 | async connect() { 72 | for (const node of this.nodes) { 73 | await node.connect(); 74 | await new Promise(r => setTimeout(r, 1000)); 75 | } 76 | } 77 | 78 | async disconnect() { 79 | for (let i = this.nodes.length - 1; i >= 0; i--) { 80 | const node = this.nodes[i]; 81 | await node.disconnect(); 82 | await new Promise(r => setTimeout(r, 1000)); 83 | } 84 | } 85 | 86 | startSync() { 87 | for (const node of this.nodes) { 88 | node.chain.synced = true; 89 | node.chain.emit('full'); 90 | node.startSync(); 91 | } 92 | } 93 | 94 | stopSync() { 95 | for (const node of this.nodes) 96 | node.stopSync(); 97 | } 98 | 99 | async generate(index, blocks) { 100 | const node = this.nodes[index]; 101 | 102 | assert(node); 103 | 104 | for (let i = 0; i < blocks; i++) { 105 | const block = await node.miner.mineBlock(); 106 | await node.chain.add(block); 107 | } 108 | } 109 | 110 | height(index) { 111 | const node = this.nodes[index]; 112 | 113 | assert(node); 114 | 115 | return node.chain.height; 116 | } 117 | 118 | async sync() { 119 | return new Promise(r => setTimeout(r, 3000)); 120 | } 121 | } 122 | 123 | module.exports = NodeContext; 124 | -------------------------------------------------------------------------------- /lib/wallet/plugin.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * plugin.js - wallet plugin for bcoin 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const EventEmitter = require('events'); 10 | const WalletDB = require('./walletdb'); 11 | const NodeClient = require('./nodeclient'); 12 | const HTTP = require('./http'); 13 | const RPC = require('./rpc'); 14 | 15 | /** 16 | * @exports wallet/plugin 17 | */ 18 | 19 | const plugin = exports; 20 | 21 | /** 22 | * Plugin 23 | * @extends EventEmitter 24 | */ 25 | 26 | class Plugin extends EventEmitter { 27 | /** 28 | * Create a plugin. 29 | * @constructor 30 | * @param {Node} node 31 | */ 32 | 33 | constructor(node) { 34 | super(); 35 | 36 | this.config = node.config.filter('wallet'); 37 | 38 | if (node.config.options.file) 39 | this.config.open('wallet.conf'); 40 | 41 | this.network = node.network; 42 | this.logger = node.logger; 43 | 44 | this.client = new NodeClient(node); 45 | 46 | this.wdb = new WalletDB({ 47 | network: this.network, 48 | logger: this.logger, 49 | workers: this.workers, 50 | client: this.client, 51 | prefix: this.config.prefix, 52 | memory: this.config.bool('memory', node.memory), 53 | maxFiles: this.config.uint('max-files'), 54 | cacheSize: this.config.mb('cache-size'), 55 | checkpoints: this.config.bool('checkpoints'), 56 | wipeNoReally: this.config.bool('wipe-no-really'), 57 | spv: node.spv 58 | }); 59 | 60 | this.rpc = new RPC(this); 61 | 62 | this.http = new HTTP({ 63 | network: this.network, 64 | logger: this.logger, 65 | node: this, 66 | ssl: this.config.bool('ssl'), 67 | keyFile: this.config.path('ssl-key'), 68 | certFile: this.config.path('ssl-cert'), 69 | host: this.config.str('http-host'), 70 | port: this.config.uint('http-port'), 71 | apiKey: this.config.str('api-key', node.config.str('api-key')), 72 | walletAuth: this.config.bool('wallet-auth'), 73 | noAuth: this.config.bool('no-auth'), 74 | cors: this.config.bool('cors'), 75 | adminToken: this.config.str('admin-token') 76 | }); 77 | 78 | this.init(); 79 | } 80 | 81 | init() { 82 | this.wdb.on('error', err => this.emit('error', err)); 83 | this.http.on('error', err => this.emit('error', err)); 84 | } 85 | 86 | async open() { 87 | await this.wdb.open(); 88 | this.rpc.wallet = this.wdb.primary; 89 | await this.http.open(); 90 | } 91 | 92 | async close() { 93 | await this.http.close(); 94 | this.rpc.wallet = null; 95 | await this.wdb.close(); 96 | } 97 | } 98 | 99 | /** 100 | * Plugin name. 101 | * @const {String} 102 | */ 103 | 104 | plugin.id = 'walletdb'; 105 | 106 | /** 107 | * Plugin initialization. 108 | * @param {Node} node 109 | * @returns {WalletDB} 110 | */ 111 | 112 | plugin.init = function init(node) { 113 | return new Plugin(node); 114 | }; 115 | -------------------------------------------------------------------------------- /docs/Design.md: -------------------------------------------------------------------------------- 1 | ## Notes on Design 2 | 3 | Bcoin is thoroughly event driven. It has a fullnode object, but Bcoin was 4 | specifically designed so the mempool, blockchain, p2p pool, and wallet database 5 | could all be used separately. All the fullnode object does is tie these things 6 | together. It's essentially a huge proxying of events. The general communication 7 | between these things looks something like this: 8 | 9 | ``` 10 | pool -> block event -> chain 11 | pool -> tx event -> mempool 12 | chain -> block event -> mempool/miner 13 | chain -> tx event -> walletdb 14 | chain -> reorg event -> walletdb/mempool/miner 15 | mempool -> tx event -> walletdb/miner 16 | miner -> block event -> chain 17 | walletdb -> tx event -> websocket server 18 | websocket server -> tx event -> websocket client 19 | http client -> tx -> http server -> mempool 20 | ``` 21 | 22 | Not only does the loose coupling make testing easier, it ensures people can 23 | utilize bcash for many use cases. 24 | 25 | ### Performance 26 | 27 | Non-javscript people reading this may think using javascript isn't a wise 28 | decision. 29 | 30 | #### Javascript 31 | 32 | Javascript is inherently slow due to how dynamic it is, but modern JITs have 33 | solved this issue using very clever optimization and dynamic recompilation 34 | techniques. v8 in some cases can [rival the speed of C++][v8] if the code is 35 | well-written. 36 | 37 | #### Concurrency 38 | 39 | Bcoin runs in node.js, so the javascript code is limited to one thread. We 40 | solve this limitation by spinning up persistent worker processes for 41 | transaction verification (webworkers when in the browser). This ensures the 42 | blockchain and mempool do not block the master process very much. It also means 43 | transaction verification can be parallelized. 44 | 45 | Strangely enough, workers are faster in the browser than they are in node since 46 | you are allowed to share memory between threads using the transferrable api 47 | (Uint8Arrays can be "transferred" to another thread). In node, you have to pipe 48 | data to another process. 49 | 50 | But of course, there is a benefit to having a multi-process architecture: the 51 | worker processes can die on their own without disturbing the master process. 52 | 53 | Bcoin uses [secp256k1-node][secp256k1-node] for ecdsa verification, which is a 54 | node.js binding to Pieter Wuille's blazingly fast [libsecp256k1][libsecp256k1] 55 | library. 56 | 57 | In the browser, bcash will use [elliptic][elliptic], the fastest javascript 58 | ecdsa implementation. It will obviously never beat C and hand-optimized 59 | assembly, but it's still usable. 60 | 61 | #### Benefits 62 | 63 | The real feature of javascript is that your code will run almost anywhere. With 64 | bcash, we now have a full node that will run on almost any browser, on laptops, 65 | on servers, on smartphones, on most devices you can imagine, even by simply 66 | visiting a webpage. 67 | 68 | [v8]: https://www.youtube.com/watch?v=UJPdhx5zTaw 69 | [libsecp256k1]: https://github.com/bitcoin-core/secp256k1 70 | [secp256k1-node]: https://github.com/cryptocoinjs/secp256k1-node 71 | [elliptic]: https://github.com/indutny/elliptic 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bcash", 3 | "version": "1.1.1", 4 | "description": "Bitcoin cash bike-shed", 5 | "license": "MIT", 6 | "repository": "git://github.com/bcoin-org/bcash.git", 7 | "homepage": "https://github.com/bcoin-org/bcash", 8 | "bugs": { 9 | "url": "https://github.com/bcoin-org/bcash/issues" 10 | }, 11 | "author": "Fedor Indutny