├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .eslintfiles ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── bench ├── bench.js ├── chacha.js ├── coins.js ├── merkle.js ├── mnemonic.js ├── script.js ├── template-sort.js ├── tx.js └── walletdb.js ├── bin ├── bcash ├── bwallet ├── cli ├── node ├── spvnode └── wallet ├── browser ├── debug.html ├── index.html ├── server.js ├── src │ ├── app.js │ └── proxysocket.js └── wsproxy.js ├── docs ├── Beginner's-Guide.md ├── CLI.md ├── Configuration.md ├── Design.md ├── Examples │ ├── client-api.js │ ├── connect-to-peer.js │ ├── connect-to-the-p2p-network.js │ ├── create-a-blockchain-and-mempool.js │ ├── create-sign-tx.js │ ├── fullnode-and-wallet.js │ ├── fullnode.js │ ├── get-tx-from-chain.js │ ├── miner-configs.js │ ├── peers-plugin.js │ ├── spv-sync-wallet.js │ └── wallet.js ├── README.md ├── Running-in-the-browser.md └── Wallet-System.md ├── etc ├── sample.conf └── sample.wallet.conf ├── jsdoc.json ├── lib ├── bcoin-browser.js ├── bcoin.js ├── blockchain │ ├── chain.js │ ├── chaindb.js │ ├── chainentry.js │ ├── common.js │ ├── index.js │ └── layout.js ├── btc │ ├── amount.js │ ├── index.js │ └── uri.js ├── coins │ ├── coinentry.js │ ├── coins.js │ ├── coinview.js │ ├── compress.js │ ├── index.js │ └── undocoins.js ├── hd │ ├── README.md │ ├── common.js │ ├── hd.js │ ├── index.js │ ├── mnemonic.js │ ├── nfkd-compat.js │ ├── nfkd.js │ ├── private.js │ ├── public.js │ ├── udata.json │ ├── unorm.js │ ├── wordlist-browser.js │ ├── wordlist.js │ └── words │ │ ├── chinese-simplified.js │ │ ├── chinese-traditional.js │ │ ├── english.js │ │ ├── french.js │ │ ├── index.js │ │ ├── italian.js │ │ ├── japanese.js │ │ └── spanish.js ├── indexer │ ├── addrindexer.js │ ├── chainclient.js │ ├── index.js │ ├── indexer.js │ ├── layout.js │ ├── nullclient.js │ ├── records.js │ └── txindexer.js ├── mempool │ ├── fees.js │ ├── index.js │ ├── layout.js │ ├── mempool.js │ └── mempoolentry.js ├── mining │ ├── common.js │ ├── cpuminer.js │ ├── index.js │ ├── mine.js │ ├── miner.js │ └── template.js ├── net │ ├── bip152.js │ ├── common.js │ ├── framer.js │ ├── hostlist.js │ ├── index.js │ ├── netaddress.js │ ├── packets.js │ ├── parser.js │ ├── peer.js │ ├── pool.js │ └── seeds │ │ ├── index.js │ │ ├── main.js │ │ └── testnet.js ├── node │ ├── fullnode.js │ ├── http.js │ ├── index.js │ ├── node.js │ ├── rpc.js │ └── spvnode.js ├── pkg.js ├── primitives │ ├── abstractblock.js │ ├── address.js │ ├── block.js │ ├── coin.js │ ├── headers.js │ ├── index.js │ ├── input.js │ ├── invitem.js │ ├── keyring.js │ ├── memblock.js │ ├── merkleblock.js │ ├── mtx.js │ ├── outpoint.js │ ├── output.js │ ├── tx.js │ └── txmeta.js ├── protocol │ ├── consensus.js │ ├── errors.js │ ├── index.js │ ├── network.js │ ├── networks.js │ ├── policy.js │ └── timedata.js ├── script │ ├── common.js │ ├── index.js │ ├── opcode.js │ ├── script.js │ ├── scripterror.js │ ├── scriptnum.js │ ├── sigcache.js │ └── stack.js ├── types.js ├── utils │ ├── binary.js │ ├── fixed.js │ ├── index.js │ └── util.js ├── wallet │ ├── account.js │ ├── client.js │ ├── common.js │ ├── http.js │ ├── index.js │ ├── layout.js │ ├── masterkey.js │ ├── node.js │ ├── nodeclient.js │ ├── nullclient.js │ ├── path.js │ ├── plugin.js │ ├── records.js │ ├── rpc.js │ ├── txdb.js │ ├── wallet.js │ ├── walletdb.js │ └── walletkey.js └── workers │ ├── child-browser.js │ ├── child.js │ ├── framer.js │ ├── index.js │ ├── jobs.js │ ├── master.js │ ├── packets.js │ ├── parent-browser.js │ ├── parent.js │ ├── parser.js │ ├── worker.js │ └── workerpool.js ├── migrate ├── README.md ├── index.js └── latest ├── package-lock.json ├── package.json ├── scripts ├── certs.sh ├── dump.js ├── gen.js └── seeds.sh ├── snap └── snapcraft.yaml ├── test ├── address-test.js ├── block-test.js ├── chain-test.js ├── coin-test.js ├── coins-test.js ├── consensus-test.js ├── data │ ├── README.md │ ├── bip69.json │ ├── block1.raw │ ├── block300025-undo.raw │ ├── block300025.raw │ ├── block426884.raw │ ├── block898352.raw │ ├── block928828-undo.raw │ ├── block928828.raw │ ├── coin1.raw │ ├── compact426884.raw │ ├── compact898352.raw │ ├── hd.json │ ├── headers1.raw │ ├── merkle300025.raw │ ├── mnemonic-english.json │ ├── mnemonic-japanese.json │ ├── script-tests.json │ ├── sighash-tests.json │ ├── tx-invalid.json │ ├── tx-valid.json │ ├── tx1-undo.raw │ ├── tx1.raw │ ├── tx10-undo.raw │ ├── tx10.raw │ ├── tx2-undo.raw │ ├── tx2.raw │ ├── tx3-undo.raw │ ├── tx3.raw │ ├── tx4-undo.raw │ ├── tx4.raw │ ├── tx6-undo.raw │ ├── tx6.raw │ ├── tx7-undo.raw │ ├── tx7.raw │ ├── tx8.raw │ └── tx9.raw ├── hd-test.js ├── headers-test.js ├── http-test.js ├── indexer-test.js ├── input-test.js ├── keyring-test.js ├── magnetic-test.js ├── mempool-test.js ├── mining-template-test.js ├── mnemonic-test.js ├── monolith-test.js ├── node-test.js ├── outpoint-test.js ├── pow-test.js ├── protocol-test.js ├── script-test.js ├── tx-test.js ├── txmeta-test.js ├── util │ ├── assert.js │ ├── common.js │ ├── memwallet.js │ ├── node-context.js │ └── reorg.js ├── utils-test.js └── wallet-test.js ├── webpack.app.js ├── webpack.browser.js └── webpack.compat.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "browsers": ["last 2 versions"] 6 | }, 7 | "useBuiltins": "usage", 8 | "loose": true 9 | }] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | docs/reference/ 3 | docker_data/ 4 | browser/bcoin* 5 | npm-debug.log 6 | coverage/ 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bench/tx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const random = require('bcrypto/lib/random'); 4 | const Address = require('../lib/primitives/address'); 5 | const TX = require('../lib/primitives/tx'); 6 | const Script = require('../lib/script/script'); 7 | const MTX = require('../lib/primitives/mtx'); 8 | const consensus = require('../lib/protocol/consensus'); 9 | const common = require('../test/util/common'); 10 | const bench = require('./bench'); 11 | 12 | const tx3 = common.readTX('tx3'); 13 | const tx5 = common.readTX('tx5'); 14 | const tx10 = common.readTX('tx10'); 15 | 16 | { 17 | const raw = tx5.getRaw(); 18 | const end = bench('parse'); 19 | 20 | for (let i = 0; i < 10000; i++) 21 | TX.fromRaw(raw); 22 | 23 | end(10000); 24 | } 25 | 26 | { 27 | const [tx, view] = tx5.getTX(); 28 | const end = bench('sigops'); 29 | 30 | for (let i = 0; i < 100000; i++) 31 | tx.getSigopsCount(view); 32 | 33 | end(100000); 34 | } 35 | 36 | { 37 | const [tx] = tx5.getTX(); 38 | const end = bench('serialize'); 39 | 40 | for (let i = 0; i < 10000; i++) { 41 | tx._raw = null; 42 | tx.toRaw(); 43 | } 44 | 45 | end(10000); 46 | } 47 | 48 | { 49 | const [tx] = tx3.getTX(); 50 | const end = bench('hash'); 51 | 52 | for (let i = 0; i < 30000; i++) { 53 | tx.hash(); 54 | tx._hash = null; 55 | } 56 | 57 | end(30000); 58 | } 59 | 60 | { 61 | const [tx] = tx5.getTX(); 62 | const end = bench('sanity'); 63 | 64 | for (let i = 0; i < 10000; i++) 65 | tx.isSane(); 66 | 67 | end(10000); 68 | } 69 | 70 | { 71 | const [tx] = tx5.getTX(); 72 | const end = bench('input hashes'); 73 | 74 | for (let i = 0; i < 10000; i++) 75 | tx.getInputHashes(); 76 | 77 | end(10000); 78 | } 79 | 80 | { 81 | const [tx] = tx5.getTX(); 82 | const end = bench('output hashes'); 83 | 84 | for (let i = 0; i < 10000; i++) 85 | tx.getOutputHashes(); 86 | 87 | end(10000); 88 | } 89 | 90 | { 91 | const [tx] = tx5.getTX(); 92 | const end = bench('all hashes'); 93 | 94 | for (let i = 0; i < 10000; i++) 95 | tx.getHashes(); 96 | 97 | end(10000); 98 | } 99 | 100 | { 101 | const [tx, view] = tx3.getTX(); 102 | const end = bench('verify'); 103 | 104 | for (let i = 0; i < 30000; i++) 105 | tx.verify(view, Script.flags.VERIFY_P2SH); 106 | 107 | end(30000 * tx.inputs.length); 108 | } 109 | 110 | { 111 | const [tx, view] = tx3.getTX(); 112 | const {script} = view.getOutputFor(tx.inputs[0]); 113 | const end = bench('sighash'); 114 | 115 | for (let i = 0; i < 1000000; i++) 116 | tx.signatureHashV0(0, script, Script.hashType.ALL); 117 | 118 | end(1000000); 119 | } 120 | 121 | { 122 | const [tx, view] = tx3.getTX(); 123 | const end = bench('fee'); 124 | 125 | for (let i = 0; i < 10000; i++) 126 | tx.getFee(view); 127 | 128 | end(10000); 129 | } 130 | 131 | { 132 | const [tx, view] = tx10.getTX(); 133 | const flags = Script.flags.VERIFY_P2SH | Script.flags.VERIFY_DERSIG; 134 | const end = bench('verify multisig'); 135 | 136 | for (let i = 0; i < 30000; i++) 137 | tx.verify(view, flags); 138 | 139 | end(30000 * tx.inputs.length); 140 | } 141 | 142 | const mtx = new MTX(); 143 | 144 | for (let i = 0; i < 100; i++) { 145 | mtx.addInput({ 146 | prevout: { 147 | hash: consensus.NULL_HASH, 148 | index: 0 149 | }, 150 | script: new Script() 151 | .pushData(Buffer.allocUnsafe(9)) 152 | .pushData(random.randomBytes(33)) 153 | .compile() 154 | }); 155 | mtx.addOutput({ 156 | address: Address.fromHash(random.randomBytes(20)), 157 | value: 0 158 | }); 159 | } 160 | 161 | const tx2 = mtx.toTX(); 162 | 163 | { 164 | const end = bench('input hashes'); 165 | 166 | for (let i = 0; i < 10000; i++) 167 | tx2.getInputHashes(); 168 | 169 | end(10000); 170 | } 171 | 172 | { 173 | const end = bench('output hashes'); 174 | 175 | for (let i = 0; i < 10000; i++) 176 | tx2.getOutputHashes(); 177 | 178 | end(10000); 179 | } 180 | 181 | { 182 | const end = bench('all hashes'); 183 | 184 | for (let i = 0; i < 10000; i++) 185 | tx2.getHashes(); 186 | 187 | end(10000); 188 | } 189 | -------------------------------------------------------------------------------- /bench/walletdb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bench = require('./bench'); 4 | const random = require('bcrypto/lib/random'); 5 | const WalletDB = require('../lib/wallet/walletdb'); 6 | const MTX = require('../lib/primitives/mtx'); 7 | const Outpoint = require('../lib/primitives/outpoint'); 8 | 9 | function dummy() { 10 | const hash = random.randomBytes(32); 11 | return new Outpoint(hash, 0); 12 | } 13 | 14 | const walletdb = new WalletDB({ 15 | name: 'wallet-test', 16 | db: 'memory', 17 | resolution: false, 18 | verify: false 19 | }); 20 | 21 | (async () => { 22 | // Open and Create 23 | await walletdb.open(); 24 | 25 | const wallet = await walletdb.create(); 26 | const addrs = []; 27 | let tx; 28 | 29 | // Accounts 30 | { 31 | const jobs = []; 32 | for (let i = 0; i < 1000; i++) 33 | jobs.push(wallet.createAccount({})); 34 | 35 | const end = bench('accounts'); 36 | const result = await Promise.all(jobs); 37 | end(1000); 38 | 39 | for (const addr of result) 40 | addrs.push(addr.receive.getAddress()); 41 | } 42 | 43 | // Keys 44 | { 45 | const jobs = []; 46 | for (let i = 0; i < 1000; i++) { 47 | for (let j = 0; j < 10; j++) 48 | jobs.push(wallet.createReceive(i)); 49 | } 50 | 51 | const end = bench('keys'); 52 | const result = await Promise.all(jobs); 53 | end(1000 * 10); 54 | 55 | for (const addr of result) 56 | addrs.push(addr.getAddress()); 57 | } 58 | 59 | // TX deposit 60 | { 61 | const jobs = []; 62 | for (let i = 0; i < 10000; i++) { 63 | const mtx = new MTX(); 64 | mtx.addOutpoint(dummy()); 65 | mtx.addOutput(addrs[(i + 0) % addrs.length], 50460); 66 | mtx.addOutput(addrs[(i + 1) % addrs.length], 50460); 67 | mtx.addOutput(addrs[(i + 2) % addrs.length], 50460); 68 | mtx.addOutput(addrs[(i + 3) % addrs.length], 50460); 69 | tx = mtx.toTX(); 70 | 71 | jobs.push(walletdb.addTX(tx)); 72 | } 73 | 74 | const end = bench('deposit'); 75 | await Promise.all(jobs); 76 | end(10000); 77 | } 78 | 79 | // TX redemption 80 | { 81 | const jobs = []; 82 | for (let i = 0; i < 10000; i++) { 83 | const mtx = new MTX(); 84 | mtx.addTX(tx, 0); 85 | mtx.addTX(tx, 1); 86 | mtx.addTX(tx, 2); 87 | mtx.addTX(tx, 3); 88 | mtx.addOutput(addrs[(i + 0) % addrs.length], 50460); 89 | mtx.addOutput(addrs[(i + 1) % addrs.length], 50460); 90 | mtx.addOutput(addrs[(i + 2) % addrs.length], 50460); 91 | mtx.addOutput(addrs[(i + 3) % addrs.length], 50460); 92 | tx = mtx.toTX(); 93 | 94 | jobs.push(walletdb.addTX(tx)); 95 | } 96 | 97 | const end = bench('redemption'); 98 | await Promise.all(jobs); 99 | end(10000); 100 | } 101 | 102 | // Balance 103 | { 104 | const end = bench('balance'); 105 | await wallet.getBalance(); 106 | end(1); 107 | } 108 | 109 | // Coins 110 | { 111 | const end = bench('coins'); 112 | await wallet.getCoins(); 113 | end(1); 114 | } 115 | 116 | // Create 117 | { 118 | const end = bench('create'); 119 | const options = { 120 | rate: 10000, 121 | outputs: [{ 122 | value: 50460, 123 | address: addrs[0] 124 | }] 125 | }; 126 | await wallet.createTX(options); 127 | end(1); 128 | } 129 | })().catch((err) => { 130 | console.error(err); 131 | process.exit(1); 132 | }); 133 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /browser/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bcoin 5 | 6 | 7 | 8 |

Bcoin

9 |

Use the console to access `global.bcoin`.

10 | 11 | 12 | -------------------------------------------------------------------------------- /browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bcoin 5 | 6 | 78 | 79 | 80 | 81 |

Bcoin, the browser full node

82 | 83 | Welcome. Your machine is currently validating the blockchain. The blocks 84 | and wallet are stored on your local disk with indexed DB. You are 85 | connecting to the actual bitcoin P2P network via a websocket->tcp 86 | proxy. Enjoy. (See the 87 | bcoin repo 89 | for more bitcoin magic). 90 | 91 |
92 |
Chain State:
93 |
Last 20 Blocks/TXs:
94 |
95 |
96 |
97 |
98 | 100 |
101 |
102 |
103 | 104 | 105 | 106 |
107 | 108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /browser/src/proxysocket.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * proxysocket.js - wsproxy socket 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 | const assert = require('assert'); 10 | const EventEmitter = require('events'); 11 | const bsock = require('bsock'); 12 | 13 | class ProxySocket extends EventEmitter { 14 | constructor(uri) { 15 | super(); 16 | 17 | this.socket = bsock.socket(); 18 | this.socket.reconnection = false; 19 | this.socket.connect(uri); 20 | 21 | this.sendBuffer = []; 22 | this.recvBuffer = []; 23 | this.paused = false; 24 | this.bytesWritten = 0; 25 | this.bytesRead = 0; 26 | this.remoteAddress = null; 27 | this.remotePort = 0; 28 | 29 | this.closed = false; 30 | 31 | this.init(); 32 | } 33 | 34 | init() { 35 | this.socket.on('error', (err) => { 36 | console.error(err); 37 | }); 38 | 39 | this.socket.bind('tcp connect', (addr, port) => { 40 | if (this.closed) 41 | return; 42 | this.remoteAddress = addr; 43 | this.remotePort = port; 44 | this.emit('connect'); 45 | }); 46 | 47 | this.socket.bind('tcp data', (data) => { 48 | data = Buffer.from(data, 'hex'); 49 | if (this.paused) { 50 | this.recvBuffer.push(data); 51 | return; 52 | } 53 | this.bytesRead += data.length; 54 | this.emit('data', data); 55 | }); 56 | 57 | this.socket.bind('tcp close', (data) => { 58 | if (this.closed) 59 | return; 60 | this.closed = true; 61 | this.emit('close'); 62 | }); 63 | 64 | this.socket.bind('tcp error', (e) => { 65 | const err = new Error(e.message); 66 | err.code = e.code; 67 | this.emit('error', err); 68 | }); 69 | 70 | this.socket.bind('tcp timeout', () => { 71 | this.emit('timeout'); 72 | }); 73 | 74 | this.socket.on('disconnect', () => { 75 | if (this.closed) 76 | return; 77 | this.closed = true; 78 | this.emit('close'); 79 | }); 80 | } 81 | 82 | connect(port, host) { 83 | this.remoteAddress = host; 84 | this.remotePort = port; 85 | 86 | if (this.closed) { 87 | this.sendBuffer.length = 0; 88 | return; 89 | } 90 | 91 | this.socket.fire('tcp connect', port, host); 92 | 93 | for (const chunk of this.sendBuffer) 94 | this.write(chunk); 95 | 96 | this.sendBuffer.length = 0; 97 | } 98 | 99 | setKeepAlive(enable, delay) { 100 | this.socket.fire('tcp keep alive', enable, delay); 101 | } 102 | 103 | setNoDelay(enable) { 104 | this.socket.fire('tcp no delay', enable); 105 | } 106 | 107 | setTimeout(timeout, callback) { 108 | this.socket.fire('tcp set timeout', timeout); 109 | if (callback) 110 | this.on('timeout', callback); 111 | } 112 | 113 | write(data, callback) { 114 | this.bytesWritten += data.length; 115 | 116 | this.socket.fire('tcp data', data.toString('hex')); 117 | 118 | if (callback) 119 | callback(); 120 | 121 | return true; 122 | } 123 | 124 | pause() { 125 | this.paused = true; 126 | } 127 | 128 | resume() { 129 | const recv = this.recvBuffer; 130 | 131 | this.paused = false; 132 | this.recvBuffer = []; 133 | 134 | for (const data of recv) { 135 | this.bytesRead += data.length; 136 | this.emit('data', data); 137 | } 138 | } 139 | 140 | destroy() { 141 | if (this.closed) 142 | return; 143 | this.closed = true; 144 | this.socket.destroy(); 145 | } 146 | 147 | static connect(uri, port, host) { 148 | const socket = new this(uri); 149 | socket.connect(port, host); 150 | return socket; 151 | } 152 | } 153 | 154 | module.exports = ProxySocket; 155 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/bcoin-browser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bcoin.js - a javascript bitcoin library. 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 | * A bcoin "environment" which exposes all 12 | * constructors for primitives, the blockchain, 13 | * mempool, wallet, etc. It also exposes a 14 | * global worker pool. 15 | * 16 | * @exports bcoin 17 | * @type {Object} 18 | */ 19 | 20 | const bcoin = exports; 21 | 22 | /** 23 | * Set the default network. 24 | * @param {String} network 25 | */ 26 | 27 | bcoin.set = function set(network) { 28 | bcoin.Network.set(network); 29 | return bcoin; 30 | }; 31 | 32 | /* 33 | * Expose 34 | */ 35 | 36 | // Blockchain 37 | bcoin.blockchain = require('./blockchain'); 38 | bcoin.Chain = require('./blockchain/chain'); 39 | bcoin.ChainEntry = require('./blockchain/chainentry'); 40 | 41 | // BTC 42 | bcoin.btc = require('./btc'); 43 | bcoin.Amount = require('./btc/amount'); 44 | bcoin.URI = require('./btc/uri'); 45 | 46 | // Coins 47 | bcoin.coins = require('./coins'); 48 | bcoin.Coins = require('./coins/coins'); 49 | bcoin.CoinEntry = require('./coins/coinentry'); 50 | bcoin.CoinView = require('./coins/coinview'); 51 | 52 | // HD 53 | bcoin.hd = require('./hd'); 54 | bcoin.HDPrivateKey = require('./hd/private'); 55 | bcoin.HDPublicKey = require('./hd/public'); 56 | bcoin.Mnemonic = require('./hd/mnemonic'); 57 | 58 | // Index 59 | bcoin.indexer = require('./indexer'); 60 | bcoin.Indexer = require('./indexer/indexer'); 61 | bcoin.ChainClient = require('./indexer/chainclient'); 62 | bcoin.TXIndexer = require('./indexer/txindexer'); 63 | bcoin.AddrIndexer = require('./indexer/addrindexer'); 64 | 65 | // Mempool 66 | bcoin.mempool = require('./mempool'); 67 | bcoin.Fees = require('./mempool/fees'); 68 | bcoin.Mempool = require('./mempool/mempool'); 69 | bcoin.MempoolEntry = require('./mempool/mempoolentry'); 70 | 71 | // Miner 72 | bcoin.mining = require('./mining'); 73 | bcoin.Miner = require('./mining/miner'); 74 | 75 | // Net 76 | bcoin.net = require('./net'); 77 | bcoin.packets = require('./net/packets'); 78 | bcoin.Peer = require('./net/peer'); 79 | bcoin.Pool = require('./net/pool'); 80 | 81 | // Node 82 | bcoin.node = require('./node'); 83 | bcoin.Node = require('./node/node'); 84 | bcoin.FullNode = require('./node/fullnode'); 85 | bcoin.SPVNode = require('./node/spvnode'); 86 | 87 | // Primitives 88 | bcoin.primitives = require('./primitives'); 89 | bcoin.Address = require('./primitives/address'); 90 | bcoin.Block = require('./primitives/block'); 91 | bcoin.Coin = require('./primitives/coin'); 92 | bcoin.Headers = require('./primitives/headers'); 93 | bcoin.Input = require('./primitives/input'); 94 | bcoin.InvItem = require('./primitives/invitem'); 95 | bcoin.KeyRing = require('./primitives/keyring'); 96 | bcoin.MerkleBlock = require('./primitives/merkleblock'); 97 | bcoin.MTX = require('./primitives/mtx'); 98 | bcoin.Outpoint = require('./primitives/outpoint'); 99 | bcoin.Output = require('./primitives/output'); 100 | bcoin.TX = require('./primitives/tx'); 101 | 102 | // Protocol 103 | bcoin.protocol = require('./protocol'); 104 | bcoin.consensus = require('./protocol/consensus'); 105 | bcoin.Network = require('./protocol/network'); 106 | bcoin.networks = require('./protocol/networks'); 107 | bcoin.policy = require('./protocol/policy'); 108 | 109 | // Script 110 | bcoin.script = require('./script'); 111 | bcoin.Opcode = require('./script/opcode'); 112 | bcoin.Script = require('./script/script'); 113 | bcoin.ScriptNum = require('./script/scriptnum'); 114 | bcoin.SigCache = require('./script/sigcache'); 115 | bcoin.Stack = require('./script/stack'); 116 | 117 | // Utils 118 | bcoin.utils = require('./utils'); 119 | bcoin.util = require('./utils/util'); 120 | 121 | // Wallet 122 | bcoin.wallet = require('./wallet'); 123 | bcoin.WalletDB = require('./wallet/walletdb'); 124 | 125 | // Workers 126 | bcoin.workers = require('./workers'); 127 | bcoin.WorkerPool = require('./workers/workerpool'); 128 | 129 | // Package Info 130 | bcoin.pkg = require('./pkg'); 131 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/coins/undocoins.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * undocoins.js - undocoins object 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 bio = require('bufio'); 11 | const CoinEntry = require('../coins/coinentry'); 12 | 13 | /** 14 | * Undo Coins 15 | * Coins need to be resurrected from somewhere 16 | * during a reorg. The undo coins store all 17 | * spent coins in a single record per block 18 | * (in a compressed format). 19 | * @alias module:coins.UndoCoins 20 | * @property {UndoCoin[]} items 21 | */ 22 | 23 | class UndoCoins { 24 | /** 25 | * Create undo coins. 26 | * @constructor 27 | */ 28 | 29 | constructor() { 30 | this.items = []; 31 | } 32 | 33 | /** 34 | * Push coin entry onto undo coin array. 35 | * @param {CoinEntry} 36 | * @returns {Number} 37 | */ 38 | 39 | push(coin) { 40 | return this.items.push(coin); 41 | } 42 | 43 | /** 44 | * Calculate undo coins size. 45 | * @returns {Number} 46 | */ 47 | 48 | getSize() { 49 | let size = 0; 50 | 51 | size += 4; 52 | 53 | for (const coin of this.items) 54 | size += coin.getSize(); 55 | 56 | return size; 57 | } 58 | 59 | /** 60 | * Serialize all undo coins. 61 | * @returns {Buffer} 62 | */ 63 | 64 | toRaw() { 65 | const size = this.getSize(); 66 | const bw = bio.write(size); 67 | 68 | bw.writeU32(this.items.length); 69 | 70 | for (const coin of this.items) 71 | coin.toWriter(bw); 72 | 73 | return bw.render(); 74 | } 75 | 76 | /** 77 | * Inject properties from serialized data. 78 | * @private 79 | * @param {Buffer} data 80 | * @returns {UndoCoins} 81 | */ 82 | 83 | fromRaw(data) { 84 | const br = bio.read(data); 85 | const count = br.readU32(); 86 | 87 | for (let i = 0; i < count; i++) 88 | this.items.push(CoinEntry.fromReader(br)); 89 | 90 | return this; 91 | } 92 | 93 | /** 94 | * Instantiate undo coins from serialized data. 95 | * @param {Buffer} data 96 | * @returns {UndoCoins} 97 | */ 98 | 99 | static fromRaw(data) { 100 | return new this().fromRaw(data); 101 | } 102 | 103 | /** 104 | * Test whether the undo coins have any members. 105 | * @returns {Boolean} 106 | */ 107 | 108 | isEmpty() { 109 | return this.items.length === 0; 110 | } 111 | 112 | /** 113 | * Render the undo coins. 114 | * @returns {Buffer} 115 | */ 116 | 117 | commit() { 118 | const raw = this.toRaw(); 119 | this.items.length = 0; 120 | return raw; 121 | } 122 | 123 | /** 124 | * Re-apply undo coins to a view, effectively unspending them. 125 | * @param {CoinView} view 126 | * @param {Outpoint} prevout 127 | */ 128 | 129 | apply(view, prevout) { 130 | const undo = this.items.pop(); 131 | 132 | assert(undo); 133 | 134 | view.addEntry(prevout, undo); 135 | } 136 | } 137 | 138 | /* 139 | * Expose 140 | */ 141 | 142 | module.exports = UndoCoins; 143 | -------------------------------------------------------------------------------- /lib/hd/README.md: -------------------------------------------------------------------------------- 1 | HD mnemonics and keys (BIP32, BIP39) for bcoin. 2 | 3 | Parts of this software were originally based on bitcore-lib: 4 | 5 | - https://github.com/bitpay/bitcore-lib/blob/master/lib/hdprivatekey.js 6 | - https://github.com/bitpay/bitcore-lib/blob/master/lib/hdpublickey.js 7 | - https://github.com/ryanxcharles/fullnode/blob/master/lib/bip32.js 8 | - https://github.com/bitpay/bitcore-mnemonic/blob/master/lib/mnemonic.js 9 | 10 | BIP32 11 | 12 | ``` 13 | Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). 14 | https://github.com/bcoin-org/bcoin 15 | 16 | Copyright (c) 2013-2015 BitPay, Inc. 17 | 18 | Parts of this software are based on Bitcoin Core 19 | Copyright (c) 2009-2015 The Bitcoin Core developers 20 | 21 | Parts of this software are based on fullnode 22 | Copyright (c) 2014 Ryan X. Charles 23 | Copyright (c) 2014 reddit, Inc. 24 | 25 | Parts of this software are based on BitcoinJS 26 | Copyright (c) 2011 Stefan Thomas 27 | 28 | Parts of this software are based on BitcoinJ 29 | Copyright (c) 2011 Google Inc. 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | ``` 49 | 50 | BIP39 51 | 52 | ``` 53 | The MIT License (MIT) 54 | 55 | Copyright (c) 2014 BitPay 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy 58 | of this software and associated documentation files (the "Software"), to deal 59 | in the Software without restriction, including without limitation the rights 60 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 61 | copies of the Software, and to permit persons to whom the Software is 62 | furnished to do so, subject to the following conditions: 63 | 64 | The above copyright notice and this permission notice shall be included in 65 | all copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 68 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 69 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 70 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 71 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 72 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 73 | SOFTWARE. 74 | ``` 75 | -------------------------------------------------------------------------------- /lib/hd/common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * common.js - common functions for hd 3 | * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const assert = require('bsert'); 10 | const LRU = require('blru'); 11 | const common = exports; 12 | 13 | /** 14 | * Index at which hardening begins. 15 | * @const {Number} 16 | * @default 17 | */ 18 | 19 | common.HARDENED = 0x80000000; 20 | 21 | /** 22 | * Min entropy bits. 23 | * @const {Number} 24 | * @default 25 | */ 26 | 27 | common.MIN_ENTROPY = 128; 28 | 29 | /** 30 | * Max entropy bits. 31 | * @const {Number} 32 | * @default 33 | */ 34 | 35 | common.MAX_ENTROPY = 512; 36 | 37 | /** 38 | * LRU cache to avoid deriving keys twice. 39 | * @type {LRU} 40 | */ 41 | 42 | common.cache = new LRU(500); 43 | 44 | /** 45 | * Parse a derivation path and return an array of indexes. 46 | * @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 47 | * @param {String} path 48 | * @param {Boolean} hard 49 | * @returns {Number[]} 50 | */ 51 | 52 | common.parsePath = function parsePath(path, hard) { 53 | assert(typeof path === 'string'); 54 | assert(typeof hard === 'boolean'); 55 | assert(path.length >= 1); 56 | assert(path.length <= 3062); 57 | 58 | const parts = path.split('/'); 59 | const root = parts[0]; 60 | 61 | if (root !== 'm' 62 | && root !== 'M' 63 | && root !== 'm\'' 64 | && root !== 'M\'') { 65 | throw new Error('Invalid path root.'); 66 | } 67 | 68 | const result = []; 69 | 70 | for (let i = 1; i < parts.length; i++) { 71 | let part = parts[i]; 72 | 73 | const hardened = part[part.length - 1] === '\''; 74 | 75 | if (hardened) 76 | part = part.slice(0, -1); 77 | 78 | if (part.length > 10) 79 | throw new Error('Path index too large.'); 80 | 81 | if (!/^\d+$/.test(part)) 82 | throw new Error('Path index is non-numeric.'); 83 | 84 | let index = parseInt(part, 10); 85 | 86 | if ((index >>> 0) !== index) 87 | throw new Error('Path index out of range.'); 88 | 89 | if (hardened) { 90 | index |= common.HARDENED; 91 | index >>>= 0; 92 | } 93 | 94 | if (!hard && (index & common.HARDENED)) 95 | throw new Error('Path index cannot be hardened.'); 96 | 97 | result.push(index); 98 | } 99 | 100 | return result; 101 | }; 102 | 103 | /** 104 | * Test whether the key is a master key. 105 | * @param {HDPrivateKey|HDPublicKey} key 106 | * @returns {Boolean} 107 | */ 108 | 109 | common.isMaster = function isMaster(key) { 110 | return key.depth === 0 111 | && key.childIndex === 0 112 | && key.parentFingerPrint === 0; 113 | }; 114 | 115 | /** 116 | * Test whether the key is (most likely) a BIP44 account key. 117 | * @param {HDPrivateKey|HDPublicKey} key 118 | * @param {Number?} account 119 | * @returns {Boolean} 120 | */ 121 | 122 | common.isAccount = function isAccount(key, account) { 123 | if (account != null) { 124 | const index = (common.HARDENED | account) >>> 0; 125 | if (key.childIndex !== index) 126 | return false; 127 | } 128 | return key.depth === 3 && (key.childIndex & common.HARDENED) !== 0; 129 | }; 130 | 131 | /** 132 | * A compressed pubkey of all zeroes. 133 | * @const {Buffer} 134 | * @default 135 | */ 136 | 137 | common.ZERO_KEY = Buffer.alloc(33, 0x00); 138 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/indexer/nullclient.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * nullclient.js - chain client 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 assert = require('bsert'); 10 | const EventEmitter = require('events'); 11 | 12 | /** 13 | * Null Client 14 | * Sort of a fake local client for separation of concerns. 15 | * @alias module:indexer.NullClient 16 | */ 17 | 18 | class NullClient extends EventEmitter { 19 | /** 20 | * Create a client. 21 | * @constructor 22 | * @param {Chain} chain 23 | */ 24 | 25 | constructor(chain) { 26 | super(); 27 | 28 | this.chain = chain; 29 | this.network = chain.network; 30 | this.opened = false; 31 | } 32 | 33 | /** 34 | * Open the client. 35 | * @returns {Promise} 36 | */ 37 | 38 | async open(options) { 39 | assert(!this.opened, 'NullClient is already open.'); 40 | this.opened = true; 41 | setImmediate(() => this.emit('connect')); 42 | } 43 | 44 | /** 45 | * Close the client. 46 | * @returns {Promise} 47 | */ 48 | 49 | async close() { 50 | assert(this.opened, 'NullClient is not open.'); 51 | this.opened = false; 52 | setImmediate(() => this.emit('disconnect')); 53 | } 54 | 55 | /** 56 | * Get chain tip. 57 | * @returns {Promise} 58 | */ 59 | 60 | async getTip() { 61 | const {hash, height, time} = this.network.genesis; 62 | return { hash, height, time }; 63 | } 64 | 65 | /** 66 | * Get chain entry. 67 | * @param {Hash} hash 68 | * @returns {Promise} - Returns {@link ChainEntry}. 69 | */ 70 | 71 | async getEntry(hash) { 72 | return { hash, height: 0, time: 0 }; 73 | } 74 | 75 | /** 76 | * Get a coin (unspents only). 77 | * @param {Hash} hash 78 | * @param {Number} index 79 | * @returns {Promise} - Returns {@link Coin}. 80 | */ 81 | 82 | async getCoin(hash, index) { 83 | return null; 84 | } 85 | 86 | /** 87 | * Get hash range. 88 | * @param {Number} start 89 | * @param {Number} end 90 | * @returns {Promise} 91 | */ 92 | 93 | async getHashes(start = -1, end = -1) { 94 | return [this.network.genesis.hash]; 95 | } 96 | 97 | /** 98 | * Get block 99 | * @param {Hash} hash 100 | * @returns {Promise} 101 | */ 102 | 103 | async getBlock(hash) { 104 | return null; 105 | } 106 | 107 | /** 108 | * Get a historical block coin viewpoint. 109 | * @param {Block} hash 110 | * @returns {Promise} - Returns {@link CoinView}. 111 | */ 112 | 113 | async getBlockView(block) { 114 | return null; 115 | } 116 | 117 | /** 118 | * Get coin viewpoint. 119 | * @param {TX} tx 120 | * @returns {Promise} - Returns {@link CoinView}. 121 | */ 122 | 123 | async getCoinView(tx) { 124 | return null; 125 | } 126 | 127 | /** 128 | * Rescan for any missed blocks. 129 | * @param {Number} start - Start block. 130 | * @returns {Promise} 131 | */ 132 | 133 | async rescan(start) { 134 | ; 135 | } 136 | } 137 | 138 | /* 139 | * Expose 140 | */ 141 | 142 | module.exports = NullClient; 143 | -------------------------------------------------------------------------------- /lib/indexer/txindexer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * txindexer.js - tx indexer 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 | const layout = require('./layout'); 11 | const TXMeta = require('../primitives/txmeta'); 12 | const Indexer = require('./indexer'); 13 | 14 | /* 15 | * TXIndexer Database Layout: 16 | * t[hash] -> extended tx 17 | */ 18 | 19 | Object.assign(layout, { 20 | t: bdb.key('t', ['hash256']) 21 | }); 22 | 23 | /** 24 | * TXIndexer 25 | * @alias module:indexer.TXIndexer 26 | * @extends Indexer 27 | */ 28 | 29 | class TXIndexer extends Indexer { 30 | /** 31 | * Create a indexer 32 | * @constructor 33 | * @param {Object} options 34 | */ 35 | 36 | constructor(options) { 37 | super('tx', options); 38 | 39 | this.db = bdb.create(this.options); 40 | } 41 | 42 | /** 43 | * Index transactions by txid. 44 | * @private 45 | * @param {ChainEntry} entry 46 | * @param {Block} block 47 | * @param {CoinView} view 48 | */ 49 | 50 | async indexBlock(entry, block, view) { 51 | const b = this.db.batch(); 52 | 53 | for (let i = 0; i < block.txs.length; i++) { 54 | const tx = block.txs[i]; 55 | const hash = tx.hash(); 56 | const meta = TXMeta.fromTX(tx, entry, i); 57 | b.put(layout.t.encode(hash), meta.toRaw()); 58 | } 59 | 60 | return b.write(); 61 | } 62 | 63 | /** 64 | * Remove transactions from index. 65 | * @private 66 | * @param {ChainEntry} entry 67 | * @param {Block} block 68 | * @param {CoinView} view 69 | */ 70 | 71 | async unindexBlock(entry, block, view) { 72 | const b = this.db.batch(); 73 | 74 | for (let i = 0; i < block.txs.length; i++) { 75 | const tx = block.txs[i]; 76 | const hash = tx.hash(); 77 | b.del(layout.t.encode(hash)); 78 | } 79 | 80 | return b.write(); 81 | } 82 | 83 | /** 84 | * Get a transaction with metadata. 85 | * @param {Hash} hash 86 | * @returns {Promise} - Returns {@link TXMeta}. 87 | */ 88 | 89 | async getMeta(hash) { 90 | const data = await this.db.get(layout.t.encode(hash)); 91 | 92 | if (!data) 93 | return null; 94 | 95 | return TXMeta.fromRaw(data); 96 | } 97 | 98 | /** 99 | * Retrieve a transaction. 100 | * @param {Hash} hash 101 | * @returns {Promise} - Returns {@link TX}. 102 | */ 103 | 104 | async getTX(hash) { 105 | const meta = await this.getMeta(hash); 106 | 107 | if (!meta) 108 | return null; 109 | 110 | return meta.tx; 111 | } 112 | 113 | /** 114 | * @param {Hash} hash 115 | * @returns {Promise} - Returns Boolean. 116 | */ 117 | 118 | async hasTX(hash) { 119 | return this.db.has(layout.t.encode(hash)); 120 | } 121 | 122 | /** 123 | * Get coin viewpoint (historical). 124 | * @param {TX} tx 125 | * @returns {Promise} - Returns {@link CoinView}. 126 | */ 127 | 128 | async getSpentView(tx) { 129 | const view = await this.client.getCoinView(tx); 130 | 131 | for (const {prevout} of tx.inputs) { 132 | if (view.hasEntry(prevout)) 133 | continue; 134 | 135 | const {hash, index} = prevout; 136 | const meta = await this.getMeta(hash); 137 | 138 | if (!meta) 139 | continue; 140 | 141 | const {tx, height} = meta; 142 | 143 | if (index < tx.outputs.length) 144 | view.addIndex(tx, index, height); 145 | } 146 | 147 | return view; 148 | } 149 | } 150 | 151 | module.exports = TXIndexer; 152 | -------------------------------------------------------------------------------- /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/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/mining/common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * common.js - mining utils 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 consensus = require('../protocol/consensus'); 11 | const BN = require('bcrypto/lib/bn.js'); 12 | 13 | /** 14 | * @exports mining/common 15 | */ 16 | 17 | const common = exports; 18 | 19 | /* 20 | * Constants 21 | */ 22 | 23 | const DIFF = 0x00000000ffff0000000000000000000000000000000000000000000000000000; 24 | const B192 = 0x1000000000000000000000000000000000000000000000000; 25 | const B128 = 0x100000000000000000000000000000000; 26 | const B64 = 0x10000000000000000; 27 | const B0 = 0x1; 28 | 29 | /** 30 | * Swap 32 bit endianness of uint256. 31 | * @param {Buffer} data 32 | * @returns {Buffer} 33 | */ 34 | 35 | common.swap32 = function swap32(data) { 36 | for (let i = 0; i < data.length; i += 4) { 37 | const field = data.readUInt32LE(i, true); 38 | data.writeUInt32BE(field, i, true); 39 | } 40 | 41 | return data; 42 | }; 43 | 44 | /** 45 | * Compare two uint256le's. 46 | * @param {Buffer} a 47 | * @param {Buffer} b 48 | * @returns {Number} 49 | */ 50 | 51 | common.rcmp = function rcmp(a, b) { 52 | assert(a.length === b.length); 53 | 54 | for (let i = a.length - 1; i >= 0; i--) { 55 | if (a[i] < b[i]) 56 | return -1; 57 | if (a[i] > b[i]) 58 | return 1; 59 | } 60 | 61 | return 0; 62 | }; 63 | 64 | /** 65 | * Convert a uint256le to a double. 66 | * @param {Buffer} target 67 | * @returns {Number} 68 | */ 69 | 70 | common.double256 = function double256(target) { 71 | let n = 0; 72 | let hi, lo; 73 | 74 | assert(target.length === 32); 75 | 76 | hi = target.readUInt32LE(28, true); 77 | lo = target.readUInt32LE(24, true); 78 | n += (hi * 0x100000000 + lo) * B192; 79 | 80 | hi = target.readUInt32LE(20, true); 81 | lo = target.readUInt32LE(16, true); 82 | n += (hi * 0x100000000 + lo) * B128; 83 | 84 | hi = target.readUInt32LE(12, true); 85 | lo = target.readUInt32LE(8, true); 86 | n += (hi * 0x100000000 + lo) * B64; 87 | 88 | hi = target.readUInt32LE(4, true); 89 | lo = target.readUInt32LE(0, true); 90 | n += (hi * 0x100000000 + lo) * B0; 91 | 92 | return n; 93 | }; 94 | 95 | /** 96 | * Calculate mining difficulty 97 | * from little-endian target. 98 | * @param {Buffer} target 99 | * @returns {Number} 100 | */ 101 | 102 | common.getDifficulty = function getDifficulty(target) { 103 | const d = DIFF; 104 | const n = common.double256(target); 105 | 106 | if (n === 0) 107 | return d; 108 | 109 | return Math.floor(d / n); 110 | }; 111 | 112 | /** 113 | * Get target from bits as a uint256le. 114 | * @param {Number} bits 115 | * @returns {Buffer} 116 | */ 117 | 118 | common.getTarget = function getTarget(bits) { 119 | const target = consensus.fromCompact(bits); 120 | 121 | if (target.isNeg()) 122 | throw new Error('Target is negative.'); 123 | 124 | if (target.isZero()) 125 | throw new Error('Target is zero.'); 126 | 127 | return target.toArrayLike(Buffer, 'le', 32); 128 | }; 129 | 130 | /** 131 | * Get bits from target. 132 | * @param {Buffer} data 133 | * @returns {Buffer} 134 | */ 135 | 136 | common.getBits = function getBits(data) { 137 | const target = new BN(data, 'le'); 138 | 139 | if (target.isZero()) 140 | throw new Error('Target is zero.'); 141 | 142 | return consensus.toCompact(target); 143 | }; 144 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /lib/net/common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * common.js - p2p 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 net/common 12 | */ 13 | 14 | const pkg = require('../pkg'); 15 | 16 | /** 17 | * Default protocol version. 18 | * @const {Number} 19 | * @default 20 | */ 21 | 22 | exports.PROTOCOL_VERSION = 70015; 23 | 24 | /** 25 | * Minimum protocol version we're willing to talk to. 26 | * @const {Number} 27 | * @default 28 | */ 29 | 30 | exports.MIN_VERSION = 70001; 31 | 32 | /** 33 | * Minimum version for getheaders. 34 | * @const {Number} 35 | * @default 36 | */ 37 | 38 | exports.HEADERS_VERSION = 31800; 39 | 40 | /** 41 | * Minimum version for pong. 42 | * @const {Number} 43 | * @default 44 | */ 45 | 46 | exports.PONG_VERSION = 60000; 47 | 48 | /** 49 | * Minimum version for bip37. 50 | * @const {Number} 51 | * @default 52 | */ 53 | 54 | exports.BLOOM_VERSION = 70011; 55 | 56 | /** 57 | * Minimum version for bip152. 58 | * @const {Number} 59 | * @default 60 | */ 61 | 62 | exports.SENDHEADERS_VERSION = 70012; 63 | 64 | /** 65 | * Minimum version for bip152. 66 | * @const {Number} 67 | * @default 68 | */ 69 | 70 | exports.COMPACT_VERSION = 70014; 71 | 72 | /** 73 | * Service bits. 74 | * @enum {Number} 75 | * @default 76 | */ 77 | 78 | exports.services = { 79 | /** 80 | * Whether network services are enabled. 81 | */ 82 | 83 | NETWORK: 1 << 0, 84 | 85 | /** 86 | * Whether the peer supports the getutxos packet. 87 | */ 88 | 89 | GETUTXO: 1 << 1, 90 | 91 | /** 92 | * Whether the peer supports BIP37. 93 | */ 94 | 95 | BLOOM: 1 << 2 96 | }; 97 | 98 | /** 99 | * Bcoin's services (we support everything). 100 | * @const {Number} 101 | * @default 102 | */ 103 | 104 | exports.LOCAL_SERVICES = 0 105 | | exports.services.NETWORK; 106 | 107 | /** 108 | * Required services (network and segwit). 109 | * @const {Number} 110 | * @default 111 | */ 112 | 113 | exports.REQUIRED_SERVICES = 0 114 | | exports.services.NETWORK; 115 | 116 | /** 117 | * Default user agent: `/bcash:[version]/`. 118 | * @const {String} 119 | * @default 120 | */ 121 | 122 | exports.USER_AGENT = `/bcash:${pkg.version}/`; 123 | 124 | /** 125 | * Max message size (~4mb with segwit, formerly 2mb) 126 | * @const {Number} 127 | * @default 128 | */ 129 | 130 | exports.MAX_MESSAGE = 1 * 1000 * 1000; 131 | 132 | /** 133 | * Amount of time to ban misbheaving peers. 134 | * @const {Number} 135 | * @default 136 | */ 137 | 138 | exports.BAN_TIME = 24 * 60 * 60; 139 | 140 | /** 141 | * Ban score threshold before ban is placed in effect. 142 | * @const {Number} 143 | * @default 144 | */ 145 | 146 | exports.BAN_SCORE = 100; 147 | 148 | /** 149 | * Create a nonce. 150 | * @returns {Buffer} 151 | */ 152 | 153 | exports.nonce = function nonce() { 154 | const data = Buffer.allocUnsafe(8); 155 | data.writeUInt32LE((Math.random() * 0x100000000) >>> 0, 0, true); 156 | data.writeUInt32LE((Math.random() * 0x100000000) >>> 0, 4, true); 157 | return data; 158 | }; 159 | 160 | /** 161 | * A compressed pubkey of all zeroes. 162 | * @const {Buffer} 163 | * @default 164 | */ 165 | 166 | exports.ZERO_KEY = Buffer.alloc(33, 0x00); 167 | 168 | /** 169 | * A 64 byte signature of all zeroes. 170 | * @const {Buffer} 171 | * @default 172 | */ 173 | 174 | exports.ZERO_SIG = Buffer.alloc(64, 0x00); 175 | 176 | /** 177 | * 8 zero bytes. 178 | * @const {Buffer} 179 | * @default 180 | */ 181 | 182 | exports.ZERO_NONCE = Buffer.alloc(8, 0x00); 183 | 184 | /** 185 | * Maximum inv/getdata size. 186 | * @const {Number} 187 | * @default 188 | */ 189 | 190 | exports.MAX_INV = 50000; 191 | 192 | /** 193 | * Maximum number of requests. 194 | * @const {Number} 195 | * @default 196 | */ 197 | 198 | exports.MAX_REQUEST = 5000; 199 | 200 | /** 201 | * Maximum number of block requests. 202 | * @const {Number} 203 | * @default 204 | */ 205 | 206 | exports.MAX_BLOCK_REQUEST = 50000 + 1000; 207 | 208 | /** 209 | * Maximum number of tx requests. 210 | * @const {Number} 211 | * @default 212 | */ 213 | 214 | exports.MAX_TX_REQUEST = 10000; 215 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/primitives/invitem.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * invitem.js - inv item object 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 | const util = require('../utils/util'); 12 | 13 | /** 14 | * Inv Item 15 | * @alias module:primitives.InvItem 16 | * @constructor 17 | * @property {InvType} type 18 | * @property {Hash} hash 19 | */ 20 | 21 | class InvItem { 22 | /** 23 | * Create an inv item. 24 | * @constructor 25 | * @param {Number} type 26 | * @param {Hash} hash 27 | */ 28 | 29 | constructor(type, hash) { 30 | this.type = type; 31 | this.hash = hash; 32 | } 33 | 34 | /** 35 | * Write inv item to buffer writer. 36 | * @param {BufferWriter} bw 37 | */ 38 | 39 | getSize() { 40 | return 36; 41 | } 42 | 43 | /** 44 | * Write inv item to buffer writer. 45 | * @param {BufferWriter} bw 46 | */ 47 | 48 | toWriter(bw) { 49 | bw.writeU32(this.type); 50 | bw.writeHash(this.hash); 51 | return bw; 52 | } 53 | 54 | /** 55 | * Serialize inv item. 56 | * @returns {Buffer} 57 | */ 58 | 59 | toRaw() { 60 | return this.toWriter(bio.write(36)).render(); 61 | } 62 | 63 | /** 64 | * Inject properties from buffer reader. 65 | * @private 66 | * @param {BufferReader} br 67 | */ 68 | 69 | fromReader(br) { 70 | this.type = br.readU32(); 71 | this.hash = br.readHash(); 72 | return this; 73 | } 74 | 75 | /** 76 | * Inject properties from serialized data. 77 | * @param {Buffer} data 78 | */ 79 | 80 | fromRaw(data) { 81 | return this.fromReader(bio.read(data)); 82 | } 83 | 84 | /** 85 | * Instantiate inv item from buffer reader. 86 | * @param {BufferReader} br 87 | * @returns {InvItem} 88 | */ 89 | 90 | static fromReader(br) { 91 | return new this().fromReader(br); 92 | } 93 | 94 | /** 95 | * Instantiate inv item from serialized data. 96 | * @param {Buffer} data 97 | * @param {String?} enc 98 | * @returns {InvItem} 99 | */ 100 | 101 | static fromRaw(data, enc) { 102 | if (typeof data === 'string') 103 | data = Buffer.from(data, enc); 104 | return new this().fromRaw(data); 105 | } 106 | 107 | /** 108 | * Test whether the inv item is a block. 109 | * @returns {Boolean} 110 | */ 111 | 112 | isBlock() { 113 | switch (this.type) { 114 | case InvItem.types.BLOCK: 115 | case InvItem.types.FILTERED_BLOCK: 116 | case InvItem.types.CMPCT_BLOCK: 117 | return true; 118 | default: 119 | return false; 120 | } 121 | } 122 | 123 | /** 124 | * Test whether the inv item is a tx. 125 | * @returns {Boolean} 126 | */ 127 | 128 | isTX() { 129 | switch (this.type) { 130 | case InvItem.types.TX: 131 | return true; 132 | default: 133 | return false; 134 | } 135 | } 136 | 137 | /** 138 | * Get little-endian hash. 139 | * @returns {Hash} 140 | */ 141 | 142 | rhash() { 143 | return util.revHex(this.hash); 144 | } 145 | } 146 | 147 | /** 148 | * Inv types. 149 | * @enum {Number} 150 | * @default 151 | */ 152 | 153 | InvItem.types = { 154 | TX: 1, 155 | BLOCK: 2, 156 | FILTERED_BLOCK: 3, 157 | CMPCT_BLOCK: 4 158 | }; 159 | 160 | /** 161 | * Inv types by value. 162 | * @const {Object} 163 | */ 164 | 165 | InvItem.typesByVal = { 166 | 1: 'TX', 167 | 2: 'BLOCK', 168 | 3: 'FILTERED_BLOCK', 169 | 4: 'CMPCT_BLOCK' 170 | }; 171 | 172 | /* 173 | * Expose 174 | */ 175 | 176 | module.exports = InvItem; 177 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/protocol/timedata.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * timedata.js - time management 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 EventEmitter = require('events'); 11 | const util = require('../utils/util'); 12 | const binary = require('../utils/binary'); 13 | 14 | /** 15 | * Time Data 16 | * An object which handles "adjusted time". This may not 17 | * look it, but this is actually a semi-consensus-critical 18 | * piece of code. It handles version packets from peers 19 | * and calculates what to offset our system clock's time by. 20 | * @alias module:protocol.TimeData 21 | * @extends EventEmitter 22 | * @property {Array} samples 23 | * @property {Object} known 24 | * @property {Number} limit 25 | * @property {Number} offset 26 | */ 27 | 28 | class TimeData extends EventEmitter { 29 | /** 30 | * Create time data. 31 | * @constructor 32 | * @param {Number} [limit=200] 33 | */ 34 | 35 | constructor(limit) { 36 | super(); 37 | 38 | if (limit == null) 39 | limit = 200; 40 | 41 | this.samples = []; 42 | this.known = new Map(); 43 | this.limit = limit; 44 | this.offset = 0; 45 | this.checked = false; 46 | } 47 | 48 | /** 49 | * Add time data. 50 | * @param {String} id 51 | * @param {Number} time 52 | */ 53 | 54 | add(id, time) { 55 | if (this.samples.length >= this.limit) 56 | return; 57 | 58 | if (this.known.has(id)) 59 | return; 60 | 61 | const sample = time - util.now(); 62 | 63 | this.known.set(id, sample); 64 | 65 | binary.insert(this.samples, sample, compare); 66 | 67 | this.emit('sample', sample, this.samples.length); 68 | 69 | if (this.samples.length >= 5 && this.samples.length % 2 === 1) { 70 | let median = this.samples[this.samples.length >>> 1]; 71 | 72 | if (Math.abs(median) >= 70 * 60) { 73 | if (!this.checked) { 74 | let match = false; 75 | 76 | for (const offset of this.samples) { 77 | if (offset !== 0 && Math.abs(offset) < 5 * 60) { 78 | match = true; 79 | break; 80 | } 81 | } 82 | 83 | if (!match) { 84 | this.checked = true; 85 | this.emit('mismatch'); 86 | } 87 | } 88 | 89 | median = 0; 90 | } 91 | 92 | this.offset = median; 93 | this.emit('offset', this.offset); 94 | } 95 | } 96 | 97 | /** 98 | * Get the current adjusted time. 99 | * @returns {Number} Adjusted Time. 100 | */ 101 | 102 | now() { 103 | return util.now() + this.offset; 104 | } 105 | 106 | /** 107 | * Adjust a timestamp. 108 | * @param {Number} time 109 | * @returns {Number} Adjusted Time. 110 | */ 111 | 112 | adjust(time) { 113 | return time + this.offset; 114 | } 115 | 116 | /** 117 | * Unadjust a timestamp. 118 | * @param {Number} time 119 | * @returns {Number} Local Time. 120 | */ 121 | 122 | local(time) { 123 | return time - this.offset; 124 | } 125 | 126 | /** 127 | * Get the current adjusted time in milliseconds. 128 | * @returns {Number} Adjusted Time. 129 | */ 130 | 131 | ms() { 132 | return Date.now() + this.offset * 1000; 133 | } 134 | } 135 | 136 | /* 137 | * Helpers 138 | */ 139 | 140 | function compare(a, b) { 141 | return a - b; 142 | } 143 | 144 | /* 145 | * Expose 146 | */ 147 | 148 | module.exports = TimeData; 149 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/script/sigcache.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * sigcache.js - signature cache 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 {BufferMap} = require('buffer-map'); 11 | const secp256k1 = require('bcrypto/lib/secp256k1'); 12 | 13 | /** 14 | * Signature cache. 15 | * @alias module:script.SigCache 16 | * @property {Number} size 17 | * @property {Hash[]} keys 18 | * @property {Object} valid 19 | */ 20 | 21 | class SigCache { 22 | /** 23 | * Create a signature cache. 24 | * @constructor 25 | * @param {Number} [size=10000] 26 | */ 27 | 28 | constructor(size) { 29 | if (size == null) 30 | size = 10000; 31 | 32 | assert((size >>> 0) === size); 33 | 34 | this.size = size; 35 | this.keys = []; 36 | this.valid = new BufferMap(); 37 | } 38 | 39 | /** 40 | * Resize the sigcache. 41 | * @param {Number} size 42 | */ 43 | 44 | resize(size) { 45 | assert((size >>> 0) === size); 46 | 47 | this.size = size; 48 | this.keys.length = 0; 49 | this.valid.clear(); 50 | } 51 | 52 | /** 53 | * Add item to the sigcache. 54 | * Potentially evict a random member. 55 | * @param {Hash} msg - Sig hash. 56 | * @param {Buffer} sig 57 | * @param {Buffer} key 58 | */ 59 | 60 | add(msg, sig, key) { 61 | if (this.size === 0) 62 | return; 63 | 64 | this.valid.set(msg, new SigCacheEntry(sig, key)); 65 | 66 | if (this.keys.length >= this.size) { 67 | const i = Math.floor(Math.random() * this.keys.length); 68 | const k = this.keys[i]; 69 | this.valid.delete(k); 70 | this.keys[i] = msg; 71 | } else { 72 | this.keys.push(msg); 73 | } 74 | } 75 | 76 | /** 77 | * Test whether the sig exists. 78 | * @param {Hash} msg - Sig hash. 79 | * @param {Buffer} sig 80 | * @param {Buffer} key 81 | * @returns {Boolean} 82 | */ 83 | 84 | has(msg, sig, key) { 85 | const entry = this.valid.get(msg); 86 | 87 | if (!entry) 88 | return false; 89 | 90 | return entry.equals(sig, key); 91 | } 92 | 93 | /** 94 | * Verify a signature, testing 95 | * it against the cache first. 96 | * @param {Buffer} msg 97 | * @param {Buffer} sig 98 | * @param {Buffer} key 99 | * @returns {Boolean} 100 | */ 101 | 102 | verify(msg, sig, key) { 103 | if (this.size === 0) 104 | return secp256k1.verifyDER(msg, sig, key); 105 | 106 | if (this.has(msg, sig, key)) 107 | return true; 108 | 109 | const result = secp256k1.verifyDER(msg, sig, key); 110 | 111 | if (!result) 112 | return false; 113 | 114 | this.add(msg, sig, key); 115 | 116 | return true; 117 | } 118 | } 119 | 120 | /** 121 | * Signature Cache Entry 122 | * @ignore 123 | * @property {Buffer} sig 124 | * @property {Buffer} key 125 | */ 126 | 127 | class SigCacheEntry { 128 | /** 129 | * Create a cache entry. 130 | * @constructor 131 | * @param {Buffer} sig 132 | * @param {Buffer} key 133 | */ 134 | 135 | constructor(sig, key) { 136 | this.sig = Buffer.from(sig); 137 | this.key = Buffer.from(key); 138 | } 139 | 140 | /** 141 | * Compare an entry to a sig and key. 142 | * @param {Buffer} sig 143 | * @param {Buffer} key 144 | * @returns {Boolean} 145 | */ 146 | 147 | equals(sig, key) { 148 | return this.sig.equals(sig) && this.key.equals(key); 149 | } 150 | } 151 | 152 | /* 153 | * Expose 154 | */ 155 | 156 | module.exports = SigCache; 157 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/wallet/common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * common.js - commonly required functions for wallet. 3 | * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). 4 | * https://github.com/bcoin-org/bcoin 5 | */ 6 | 7 | 'use strict'; 8 | 9 | const {BufferMap} = require('buffer-map'); 10 | 11 | /** 12 | * @exports wallet/common 13 | */ 14 | 15 | const common = exports; 16 | 17 | /** 18 | * Test whether a string is eligible 19 | * to be used as a name or ID. 20 | * @param {String} key 21 | * @returns {Boolean} 22 | */ 23 | 24 | common.isName = function isName(key) { 25 | if (typeof key !== 'string') 26 | return false; 27 | 28 | if (key.length === 0) 29 | return false; 30 | 31 | if (!/^[\-\._0-9A-Za-z]+$/.test(key)) 32 | return false; 33 | 34 | // Prevents __proto__ 35 | // from being used. 36 | switch (key[0]) { 37 | case '_': 38 | case '-': 39 | case '.': 40 | return false; 41 | } 42 | 43 | switch (key[key.length - 1]) { 44 | case '_': 45 | case '-': 46 | case '.': 47 | return false; 48 | } 49 | 50 | return key.length >= 1 && key.length <= 40; 51 | }; 52 | 53 | /** 54 | * Sort an array of transactions by time. 55 | * @param {TX[]} txs 56 | * @returns {TX[]} 57 | */ 58 | 59 | common.sortTX = function sortTX(txs) { 60 | return txs.sort((a, b) => { 61 | return a.mtime - b.mtime; 62 | }); 63 | }; 64 | 65 | /** 66 | * Sort an array of coins by height. 67 | * @param {Coin[]} txs 68 | * @returns {Coin[]} 69 | */ 70 | 71 | common.sortCoins = function sortCoins(coins) { 72 | return coins.sort((a, b) => { 73 | a = a.height === -1 ? 0x7fffffff : a.height; 74 | b = b.height === -1 ? 0x7fffffff : b.height; 75 | return a - b; 76 | }); 77 | }; 78 | 79 | /** 80 | * Sort an array of transactions in dependency order. 81 | * @param {TX[]} txs 82 | * @returns {TX[]} 83 | */ 84 | 85 | common.sortDeps = function sortDeps(txs) { 86 | const map = new BufferMap(); 87 | 88 | for (const tx of txs) { 89 | const hash = tx.hash(); 90 | map.set(hash, tx); 91 | } 92 | 93 | const depMap = new BufferMap(); 94 | const depCount = new BufferMap(); 95 | const top = []; 96 | 97 | for (const [hash, tx] of map) { 98 | depCount.set(hash, 0); 99 | 100 | let hasDeps = false; 101 | 102 | for (const input of tx.inputs) { 103 | const prev = input.prevout.hash; 104 | 105 | if (!map.has(prev)) 106 | continue; 107 | 108 | const count = depCount.get(hash); 109 | depCount.set(hash, count + 1); 110 | hasDeps = true; 111 | 112 | if (!depMap.has(prev)) 113 | depMap.set(prev, []); 114 | 115 | depMap.get(prev).push(tx); 116 | } 117 | 118 | if (hasDeps) 119 | continue; 120 | 121 | top.push(tx); 122 | } 123 | 124 | const result = []; 125 | 126 | for (const tx of top) { 127 | const deps = depMap.get(tx.hash()); 128 | 129 | result.push(tx); 130 | 131 | if (!deps) 132 | continue; 133 | 134 | for (const tx of deps) { 135 | let count = depCount.get(tx.hash()); 136 | 137 | if (--count === 0) 138 | top.push(tx); 139 | 140 | depCount.set(tx.hash(), count); 141 | } 142 | } 143 | 144 | return result; 145 | }; 146 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/wallet/node.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * server.js - wallet server 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 Node = require('../node/node'); 11 | const WalletDB = require('./walletdb'); 12 | const HTTP = require('./http'); 13 | const Client = require('./client'); 14 | const RPC = require('./rpc'); 15 | 16 | /** 17 | * Wallet Node 18 | * @extends Node 19 | */ 20 | 21 | class WalletNode extends Node { 22 | /** 23 | * Create a wallet node. 24 | * @constructor 25 | * @param {Object?} options 26 | */ 27 | 28 | constructor(options) { 29 | super('bcash', 'wallet.conf', 'wallet.log', options); 30 | 31 | this.opened = false; 32 | 33 | this.client = new Client({ 34 | network: this.network, 35 | url: this.config.str('node-url'), 36 | host: this.config.str('node-host'), 37 | port: this.config.uint('node-port', this.network.rpcPort), 38 | ssl: this.config.bool('node-ssl'), 39 | apiKey: this.config.str('node-api-key') 40 | }); 41 | 42 | this.wdb = new WalletDB({ 43 | network: this.network, 44 | logger: this.logger, 45 | workers: this.workers, 46 | client: this.client, 47 | prefix: this.config.prefix, 48 | memory: this.config.bool('memory'), 49 | maxFiles: this.config.uint('max-files'), 50 | cacheSize: this.config.mb('cache-size'), 51 | checkpoints: this.config.bool('checkpoints'), 52 | wipeNoReally: this.config.bool('wipe-no-really'), 53 | spv: this.config.bool('spv') 54 | }); 55 | 56 | this.rpc = new RPC(this); 57 | 58 | this.http = new HTTP({ 59 | network: this.network, 60 | logger: this.logger, 61 | node: this, 62 | prefix: this.config.prefix, 63 | ssl: this.config.bool('ssl'), 64 | keyFile: this.config.path('ssl-key'), 65 | certFile: this.config.path('ssl-cert'), 66 | host: this.config.str('http-host'), 67 | port: this.config.uint('http-port'), 68 | apiKey: this.config.str('api-key'), 69 | walletAuth: this.config.bool('wallet-auth'), 70 | noAuth: this.config.bool('no-auth'), 71 | cors: this.config.bool('cors'), 72 | adminToken: this.config.str('admin-token') 73 | }); 74 | 75 | this.init(); 76 | } 77 | 78 | /** 79 | * Initialize the node. 80 | * @private 81 | */ 82 | 83 | init() { 84 | this.wdb.on('error', err => this.error(err)); 85 | this.http.on('error', err => this.error(err)); 86 | 87 | this.loadPlugins(); 88 | } 89 | 90 | /** 91 | * Open the node and all its child objects, 92 | * wait for the database to load. 93 | * @returns {Promise} 94 | */ 95 | 96 | async open() { 97 | assert(!this.opened, 'WalletNode is already open.'); 98 | this.opened = true; 99 | 100 | await this.handlePreopen(); 101 | await this.wdb.open(); 102 | 103 | this.rpc.wallet = this.wdb.primary; 104 | 105 | await this.openPlugins(); 106 | 107 | await this.http.open(); 108 | await this.handleOpen(); 109 | 110 | this.logger.info('Wallet node is loaded.'); 111 | } 112 | 113 | /** 114 | * Close the node, wait for the database to close. 115 | * @returns {Promise} 116 | */ 117 | 118 | async close() { 119 | assert(this.opened, 'WalletNode is not open.'); 120 | this.opened = false; 121 | 122 | await this.handlePreclose(); 123 | await this.http.close(); 124 | 125 | await this.closePlugins(); 126 | 127 | this.rpc.wallet = null; 128 | 129 | await this.wdb.close(); 130 | await this.handleClose(); 131 | } 132 | } 133 | 134 | /* 135 | * Expose 136 | */ 137 | 138 | module.exports = WalletNode; 139 | -------------------------------------------------------------------------------- /lib/wallet/nullclient.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * nullclient.js - node client 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 | * Null Client 14 | * Sort of a fake local client for separation of concerns. 15 | * @alias module:node.NullClient 16 | */ 17 | 18 | class NullClient extends EventEmitter { 19 | /** 20 | * Create a client. 21 | * @constructor 22 | */ 23 | 24 | constructor(wdb) { 25 | super(); 26 | 27 | this.wdb = wdb; 28 | this.network = wdb.network; 29 | this.opened = false; 30 | } 31 | 32 | /** 33 | * Open the client. 34 | * @returns {Promise} 35 | */ 36 | 37 | async open(options) { 38 | assert(!this.opened, 'NullClient is already open.'); 39 | this.opened = true; 40 | setImmediate(() => this.emit('connect')); 41 | } 42 | 43 | /** 44 | * Close the client. 45 | * @returns {Promise} 46 | */ 47 | 48 | async close() { 49 | assert(this.opened, 'NullClient is not open.'); 50 | this.opened = false; 51 | setImmediate(() => this.emit('disconnect')); 52 | } 53 | 54 | /** 55 | * Add a listener. 56 | * @param {String} type 57 | * @param {Function} handler 58 | */ 59 | 60 | bind(type, handler) { 61 | return this.on(type, handler); 62 | } 63 | 64 | /** 65 | * Add a listener. 66 | * @param {String} type 67 | * @param {Function} handler 68 | */ 69 | 70 | hook(type, handler) { 71 | return this.on(type, handler); 72 | } 73 | 74 | /** 75 | * Get chain tip. 76 | * @returns {Promise} 77 | */ 78 | 79 | async getTip() { 80 | const {hash, height, time} = this.network.genesis; 81 | return { hash, height, time }; 82 | } 83 | 84 | /** 85 | * Get chain entry. 86 | * @param {Hash} hash 87 | * @returns {Promise} 88 | */ 89 | 90 | async getEntry(hash) { 91 | return { hash, height: 0, time: 0 }; 92 | } 93 | 94 | /** 95 | * Send a transaction. Do not wait for promise. 96 | * @param {TX} tx 97 | * @returns {Promise} 98 | */ 99 | 100 | async send(tx) { 101 | this.wdb.emit('send', tx); 102 | } 103 | 104 | /** 105 | * Set bloom filter. 106 | * @param {Bloom} filter 107 | * @returns {Promise} 108 | */ 109 | 110 | async setFilter(filter) { 111 | this.wdb.emit('set filter', filter); 112 | } 113 | 114 | /** 115 | * Add data to filter. 116 | * @param {Buffer} data 117 | * @returns {Promise} 118 | */ 119 | 120 | async addFilter(data) { 121 | this.wdb.emit('add filter', data); 122 | } 123 | 124 | /** 125 | * Reset filter. 126 | * @returns {Promise} 127 | */ 128 | 129 | async resetFilter() { 130 | this.wdb.emit('reset filter'); 131 | } 132 | 133 | /** 134 | * Esimate smart fee. 135 | * @param {Number?} blocks 136 | * @returns {Promise} 137 | */ 138 | 139 | async estimateFee(blocks) { 140 | return this.network.feeRate; 141 | } 142 | 143 | /** 144 | * Get hash range. 145 | * @param {Number} start 146 | * @param {Number} end 147 | * @returns {Promise} 148 | */ 149 | 150 | async getHashes(start = -1, end = -1) { 151 | return [this.network.genesis.hash]; 152 | } 153 | 154 | /** 155 | * Rescan for any missed transactions. 156 | * @param {Number|Hash} start - Start block. 157 | * @param {Bloom} filter 158 | * @param {Function} iter - Iterator. 159 | * @returns {Promise} 160 | */ 161 | 162 | async rescan(start) { 163 | ; 164 | } 165 | } 166 | 167 | /* 168 | * Expose 169 | */ 170 | 171 | module.exports = NullClient; 172 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /migrate/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/migrate/index.js -------------------------------------------------------------------------------- /migrate/latest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | console.error('No migrations available.'); 6 | process.exit(1); 7 | -------------------------------------------------------------------------------- /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 ", 12 | "contributors": [ 13 | "Christopher Jeffrey " 14 | ], 15 | "keywords": [ 16 | "bcash", 17 | "bitcoin", 18 | "blockchain", 19 | "cryptocurrency", 20 | "wallet" 21 | ], 22 | "engines": { 23 | "node": ">=8.0.0" 24 | }, 25 | "dependencies": { 26 | "bcfg": "^0.1.5", 27 | "bclient": "^0.1.6", 28 | "bcrypto": "^3.0.2", 29 | "bdb": "^1.1.5", 30 | "bdns": "^0.1.4", 31 | "bevent": "^0.1.4", 32 | "bfile": "^0.1.4", 33 | "bfilter": "^1.0.4", 34 | "bheep": "^0.1.4", 35 | "binet": "^0.3.4", 36 | "blgr": "^0.1.5", 37 | "blru": "^0.1.5", 38 | "blst": "^0.1.4", 39 | "bmutex": "^0.1.5", 40 | "bsert": "0.0.8", 41 | "bsip": "^0.1.5", 42 | "bsock": "^0.1.5", 43 | "bsocks": "^0.2.4", 44 | "bstring": "^0.3.5", 45 | "btcp": "^0.1.4", 46 | "buffer-map": "0.0.5", 47 | "bufio": "^1.0.4", 48 | "bupnp": "^0.2.5", 49 | "bval": "^0.1.5", 50 | "bweb": "^0.1.7", 51 | "mrmr": "^0.1.5", 52 | "n64": "^0.2.6" 53 | }, 54 | "devDependencies": { 55 | "bmocha": "^1.0.0" 56 | }, 57 | "main": "./lib/bcoin.js", 58 | "bin": { 59 | "bcash": "./bin/bcash", 60 | "bcash-node": "./bin/node", 61 | "bcash-spvnode": "./bin/spvnode", 62 | "bcash-wallet": "./bin/bwallet" 63 | }, 64 | "scripts": { 65 | "browserify": "browserify -s bcash lib/bcoin-browser.js | uglifyjs -c > bcash.js", 66 | "clean": "rm -f {browser/,}{bcash.js,bcash-worker.js,app.js,worker.js}", 67 | "docs": "jsdoc -c jsdoc.json", 68 | "lint": "eslint $(cat .eslintfiles) || exit 0", 69 | "lint-file": "eslint", 70 | "lint-ci": "eslint $(cat .eslintfiles)", 71 | "test": "bmocha --reporter spec test/*.js", 72 | "test-browser": "NODE_BACKEND=js bmocha --reporter spec test/*.js", 73 | "test-file": "bmocha --reporter spec", 74 | "test-file-browser": "NODE_BACKEND=js bmocha --reporter spec", 75 | "test-ci": "istanbul cover --report lcovonly node_modules/.bin/bmocha -- --reporter spec test/*-test.js", 76 | "webpack": "webpack --mode production --config webpack.browser.js", 77 | "webpack-browser": "webpack --mode production --config webpack.browser.js", 78 | "webpack-compat": "webpack --mode production --config webpack.compat.js", 79 | "webpack-app": "webpack --mode production --config webpack.app.js" 80 | }, 81 | "browser": { 82 | "./lib/hd/nfkd": "./lib/hd/nfkd-compat.js", 83 | "./lib/hd/wordlist": "./lib/hd/wordlist-browser.js", 84 | "./lib/workers/child": "./lib/workers/child-browser.js", 85 | "./lib/workers/parent": "./lib/workers/parent-browser.js", 86 | "./lib/bcash": "./lib/bcoin-browser.js" 87 | }, 88 | "bcoin": { 89 | "upstream": "15b024a3928ea8a54732bb2f5177ab1668468258" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /scripts/gen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const consensus = require('../lib/protocol/consensus'); 4 | const TX = require('../lib/primitives/tx'); 5 | const Block = require('../lib/primitives/block'); 6 | const Script = require('../lib/script/script'); 7 | 8 | function createGenesisBlock(options) { 9 | let flags = options.flags; 10 | let key = options.key; 11 | let reward = options.reward; 12 | 13 | if (!flags) { 14 | flags = Buffer.from( 15 | 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks', 16 | 'ascii'); 17 | } 18 | 19 | if (!key) { 20 | key = Buffer.from('' 21 | + '04678afdb0fe5548271967f1a67130b7105cd6a828e039' 22 | + '09a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c3' 23 | + '84df7ba0b8d578a4c702b6bf11d5f', 'hex'); 24 | } 25 | 26 | if (!reward) 27 | reward = 50 * consensus.COIN; 28 | 29 | const tx = new TX({ 30 | version: 1, 31 | inputs: [{ 32 | prevout: { 33 | hash: consensus.ZERO_HASH, 34 | index: 0xffffffff 35 | }, 36 | script: Script() 37 | .pushInt(486604799) 38 | .pushPush(Buffer.from([4])) 39 | .pushData(flags) 40 | .compile(), 41 | sequence: 0xffffffff 42 | }], 43 | outputs: [{ 44 | value: reward, 45 | script: Script.fromPubkey(key) 46 | }], 47 | locktime: 0 48 | }); 49 | 50 | const block = new Block({ 51 | version: options.version, 52 | prevBlock: consensus.ZERO_HASH, 53 | merkleRoot: tx.hash(), 54 | time: options.time, 55 | bits: options.bits, 56 | nonce: options.nonce, 57 | height: 0 58 | }); 59 | 60 | block.txs.push(tx); 61 | 62 | return block; 63 | } 64 | 65 | const main = createGenesisBlock({ 66 | version: 1, 67 | time: 1231006505, 68 | bits: 486604799, 69 | nonce: 2083236893 70 | }); 71 | 72 | const testnet = createGenesisBlock({ 73 | version: 1, 74 | time: 1296688602, 75 | bits: 486604799, 76 | nonce: 414098458 77 | }); 78 | 79 | const regtest = createGenesisBlock({ 80 | version: 1, 81 | time: 1296688602, 82 | bits: 545259519, 83 | nonce: 2 84 | }); 85 | 86 | const segnet3 = createGenesisBlock({ 87 | version: 1, 88 | time: 1452831101, 89 | bits: 486604799, 90 | nonce: 0 91 | }); 92 | 93 | const segnet4 = createGenesisBlock({ 94 | version: 1, 95 | time: 1452831101, 96 | bits: 503447551, 97 | nonce: 0 98 | }); 99 | 100 | const btcd = createGenesisBlock({ 101 | version: 1, 102 | time: 1401292357, 103 | bits: 545259519, 104 | nonce: 2 105 | }); 106 | 107 | console.log(main); 108 | console.log(''); 109 | console.log(testnet); 110 | console.log(''); 111 | console.log(regtest); 112 | console.log(''); 113 | console.log(segnet3); 114 | console.log(''); 115 | console.log(segnet4); 116 | console.log(''); 117 | console.log(''); 118 | console.log('main hash: %s', main.rhash()); 119 | console.log('main raw: %s', main.toRaw().toString('hex')); 120 | console.log(''); 121 | console.log('testnet hash: %s', testnet.rhash()); 122 | console.log('testnet raw: %s', testnet.toRaw().toString('hex')); 123 | console.log(''); 124 | console.log('regtest hash: %s', regtest.rhash()); 125 | console.log('regtest raw: %s', regtest.toRaw().toString('hex')); 126 | console.log(''); 127 | console.log('segnet3 hash: %s', segnet3.rhash()); 128 | console.log('segnet3 raw: %s', segnet3.toRaw().toString('hex')); 129 | console.log(''); 130 | console.log('segnet4 hash: %s', segnet4.rhash()); 131 | console.log('segnet4 raw: %s', segnet4.toRaw().toString('hex')); 132 | console.log(''); 133 | console.log('btcd simnet hash: %s', btcd.rhash()); 134 | console.log('btcd simnet raw: %s', btcd.toRaw().toString('hex')); 135 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/coins-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | /* eslint prefer-arrow-callback: "off" */ 3 | 4 | 'use strict'; 5 | 6 | const bio = require('bufio'); 7 | const assert = require('./util/assert'); 8 | const Output = require('../lib/primitives/output'); 9 | const Input = require('../lib/primitives/input'); 10 | const Outpoint = require('../lib/primitives/outpoint'); 11 | const CoinView = require('../lib/coins/coinview'); 12 | const CoinEntry = require('../lib/coins/coinentry'); 13 | const common = require('./util/common'); 14 | 15 | const tx1 = common.readTX('tx1'); 16 | 17 | function reserialize(coin) { 18 | const raw = coin.toRaw(); 19 | const entry = CoinEntry.fromRaw(raw); 20 | entry.raw = null; 21 | return CoinEntry.fromRaw(entry.toRaw()); 22 | } 23 | 24 | function deepCoinsEqual(a, b) { 25 | assert.strictEqual(a.version, b.version); 26 | assert.strictEqual(a.height, b.height); 27 | assert.strictEqual(a.coinbase, b.coinbase); 28 | assert.bufferEqual(a.raw, b.raw); 29 | } 30 | 31 | describe('Coins', function() { 32 | it('should instantiate coinview from tx', () => { 33 | const [tx] = tx1.getTX(); 34 | const hash = tx.hash(); 35 | const view = new CoinView(); 36 | const prevout = new Outpoint(hash, 0); 37 | const input = Input.fromOutpoint(prevout); 38 | 39 | view.addTX(tx, 1); 40 | 41 | const coins = view.get(hash); 42 | assert.strictEqual(coins.outputs.size, tx.outputs.length); 43 | 44 | const entry = coins.get(0); 45 | assert(entry); 46 | 47 | assert.strictEqual(entry.version, 1); 48 | assert.strictEqual(entry.height, 1); 49 | assert.strictEqual(entry.coinbase, false); 50 | assert.strictEqual(entry.raw, null); 51 | assert.instanceOf(entry.output, Output); 52 | assert.strictEqual(entry.spent, false); 53 | 54 | const output = view.getOutputFor(input); 55 | assert(output); 56 | 57 | deepCoinsEqual(entry, reserialize(entry)); 58 | }); 59 | 60 | it('should spend an output', () => { 61 | const [tx] = tx1.getTX(); 62 | const hash = tx.hash(); 63 | const view = new CoinView(); 64 | 65 | view.addTX(tx, 1); 66 | 67 | const coins = view.get(hash); 68 | assert(coins); 69 | 70 | const length = coins.outputs.size; 71 | 72 | view.spendEntry(new Outpoint(hash, 0)); 73 | 74 | assert.strictEqual(view.get(hash), coins); 75 | 76 | const entry = coins.get(0); 77 | assert(entry); 78 | assert(entry.spent); 79 | 80 | deepCoinsEqual(entry, reserialize(entry)); 81 | assert.strictEqual(coins.outputs.size, length); 82 | 83 | assert.strictEqual(view.undo.items.length, 1); 84 | }); 85 | 86 | it('should handle coin view', () => { 87 | const [tx, view] = tx1.getTX(); 88 | 89 | const size = view.getSize(tx); 90 | const bw = bio.write(size); 91 | const raw = view.toWriter(bw, tx).render(); 92 | const br = bio.read(raw); 93 | const res = CoinView.fromReader(br, tx); 94 | 95 | const prev = tx.inputs[0].prevout; 96 | const coins = res.get(prev.hash); 97 | 98 | assert.strictEqual(coins.outputs.size, 1); 99 | assert.strictEqual(coins.get(0), null); 100 | deepCoinsEqual(coins.get(1), reserialize(coins.get(1))); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/data/block1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block1.raw -------------------------------------------------------------------------------- /test/data/block300025-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block300025-undo.raw -------------------------------------------------------------------------------- /test/data/block300025.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block300025.raw -------------------------------------------------------------------------------- /test/data/block426884.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block426884.raw -------------------------------------------------------------------------------- /test/data/block898352.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block898352.raw -------------------------------------------------------------------------------- /test/data/block928828-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block928828-undo.raw -------------------------------------------------------------------------------- /test/data/block928828.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/block928828.raw -------------------------------------------------------------------------------- /test/data/coin1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/coin1.raw -------------------------------------------------------------------------------- /test/data/compact426884.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/compact426884.raw -------------------------------------------------------------------------------- /test/data/compact898352.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/compact898352.raw -------------------------------------------------------------------------------- /test/data/headers1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/headers1.raw -------------------------------------------------------------------------------- /test/data/merkle300025.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/merkle300025.raw -------------------------------------------------------------------------------- /test/data/tx1-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx1-undo.raw -------------------------------------------------------------------------------- /test/data/tx1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx1.raw -------------------------------------------------------------------------------- /test/data/tx10-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx10-undo.raw -------------------------------------------------------------------------------- /test/data/tx10.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx10.raw -------------------------------------------------------------------------------- /test/data/tx2-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx2-undo.raw -------------------------------------------------------------------------------- /test/data/tx2.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx2.raw -------------------------------------------------------------------------------- /test/data/tx3-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx3-undo.raw -------------------------------------------------------------------------------- /test/data/tx3.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx3.raw -------------------------------------------------------------------------------- /test/data/tx4-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx4-undo.raw -------------------------------------------------------------------------------- /test/data/tx4.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx4.raw -------------------------------------------------------------------------------- /test/data/tx6-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx6-undo.raw -------------------------------------------------------------------------------- /test/data/tx6.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx6.raw -------------------------------------------------------------------------------- /test/data/tx7-undo.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx7-undo.raw -------------------------------------------------------------------------------- /test/data/tx7.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx7.raw -------------------------------------------------------------------------------- /test/data/tx8.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx8.raw -------------------------------------------------------------------------------- /test/data/tx9.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcoin-org/bcash/867f219b4da3978703acbb9b7077cc84d75b293f/test/data/tx9.raw -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/outpoint-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | 'use strict'; 4 | 5 | const Outpoint = require('../lib/primitives/outpoint'); 6 | const assert = require('./util/assert'); 7 | const common = require('./util/common'); 8 | const util = require('../lib/utils/util'); 9 | const TX = require('../lib/primitives/tx'); 10 | const OUTPOINT_SIZE = 36; 11 | 12 | describe('Outpoint', () => { 13 | let raw1, tx1, out1; 14 | beforeEach(() => { 15 | tx1 = common.readTX('tx1').getRaw(); 16 | raw1 = tx1.slice(5, 5+OUTPOINT_SIZE); 17 | out1 = Outpoint.fromRaw(raw1); 18 | }); 19 | 20 | it('should clone the outpoint correctly', () => { 21 | const out1 = Outpoint.fromRaw(raw1); 22 | const clone = out1.clone(); 23 | const equals = out1.equals(clone); 24 | 25 | assert.strictEqual(out1 !== clone, true); 26 | assert.strictEqual(equals, true); 27 | }); 28 | 29 | it('should create outpoint from options object', () => { 30 | const options = {}; 31 | options.hash = out1.hash; 32 | options.index = out1.index; 33 | 34 | const newOut = Outpoint.fromOptions(options); 35 | assert(newOut.equals(out1), true); 36 | }); 37 | 38 | it('should check hash and index are equal', () => { 39 | const out1Clone = Outpoint.fromOptions(Object.assign(out1, {})); 40 | 41 | assert(out1Clone.hash === out1.hash); 42 | assert(out1Clone.index === out1.index); 43 | }); 44 | 45 | it('should compare the indexes between outpoints', () => { 46 | const out1RevHash = out1.clone(); 47 | out1RevHash.hash = Buffer.from(out1RevHash.hash); 48 | out1RevHash.hash[0] = 0; 49 | 50 | const out1AdjIndex = out1.clone(); 51 | out1AdjIndex.index += 1; 52 | 53 | const index1 = out1.index; 54 | const index2 = out1AdjIndex.index; 55 | // assert that it compares txid first 56 | assert(out1.compare(out1RevHash) !== 0, 'txid wasn\'t compared correctly'); 57 | assert(out1.compare(out1) === 0); 58 | assert(out1.compare(out1AdjIndex) === index1 - index2); 59 | }); 60 | 61 | it('should detect if the outpoint is null', () => { 62 | const rawHash = '00000000000000000000000000000000000000000000' + 63 | '00000000000000000000'; 64 | const rawIndex = 'ffffffff'; 65 | const nullOut = Outpoint.fromRaw(Buffer.from(rawHash + rawIndex, 'hex')); 66 | assert(nullOut.isNull(), true); 67 | }); 68 | 69 | it('should retrieve little endian hash', () => { 70 | assert.strictEqual(out1.rhash(), util.revHex(out1.hash)); 71 | assert.strictEqual(out1.txid(), util.revHex(out1.hash)); 72 | }); 73 | 74 | it('should serialize to a key suitable for hash table', () => { 75 | const expected = out1.toRaw(); 76 | const actual = out1.toKey(); 77 | assert.bufferEqual(expected, actual); 78 | }); 79 | 80 | it('should inject properties from hash table key', () => { 81 | const key = out1.toKey(); 82 | const fromKey = Outpoint.fromKey(key); 83 | assert(out1.equals(fromKey), true); 84 | }); 85 | 86 | it('should return a size of 36', () => { 87 | assert(out1.getSize() === OUTPOINT_SIZE, true); 88 | }); 89 | 90 | it('should create an outpoint from JSON', () => { 91 | const json = { 92 | hash: out1.txid(), 93 | index: out1.index 94 | }; 95 | const fromJSON = Outpoint.fromJSON(json); 96 | 97 | assert.deepEqual(out1, fromJSON); 98 | }); 99 | 100 | it('should return an object with reversed hash', () => { 101 | const hash = out1.hash; 102 | const index = out1.index; 103 | 104 | const expected = { 105 | hash: util.revHex(hash), 106 | index 107 | }; 108 | assert.deepEqual(expected, out1.toJSON()); 109 | }); 110 | 111 | it('should instantiate an outpoint from a tx', () => { 112 | const tx = TX.fromRaw(tx1); 113 | const index = 0; 114 | const fromTX = Outpoint.fromTX(tx, index); 115 | 116 | assert.bufferEqual(fromTX.hash, tx.hash()); 117 | assert.strictEqual(fromTX.index, index); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------