├── .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 |
101 |
102 |
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 |
--------------------------------------------------------------------------------