From a534d7c4290f191af942e413b29aa364fd95c42b Mon Sep 17 00:00:00 2001 From: Node Date: Thu, 20 Jul 2017 22:05:23 +0400 Subject: [PATCH] Move docs from wiki --- .gitignore | 2 +- docs/Beginner's-Guide.md | 147 +++ docs/CLI.md | 235 +++++ docs/Configuration.md | 97 ++ docs/Design.md | 71 ++ docs/Example-Connecting-to-the-P2P-Network.md | 86 ++ ...ample-Creating-a-Blockchain-and-Mempool.md | 35 + docs/Example-Fullnode-Object.md | 72 ++ docs/Example-SPV-Sync.md | 44 + docs/README.md | 42 + docs/REST-RPC-API.md | 954 ++++++++++++++++++ docs/Running-in-the-browser.md | 16 + docs/Scripting.md | 49 + docs/Wallet-System.md | 22 + docs/Working-with-transactions.md | 153 +++ jsdoc.json | 2 +- 16 files changed, 2025 insertions(+), 2 deletions(-) create mode 100644 docs/Beginner's-Guide.md create mode 100644 docs/CLI.md create mode 100644 docs/Configuration.md create mode 100644 docs/Design.md create mode 100644 docs/Example-Connecting-to-the-P2P-Network.md create mode 100644 docs/Example-Creating-a-Blockchain-and-Mempool.md create mode 100644 docs/Example-Fullnode-Object.md create mode 100644 docs/Example-SPV-Sync.md create mode 100644 docs/README.md create mode 100644 docs/REST-RPC-API.md create mode 100644 docs/Running-in-the-browser.md create mode 100644 docs/Scripting.md create mode 100644 docs/Wallet-System.md create mode 100644 docs/Working-with-transactions.md diff --git a/.gitignore b/.gitignore index 2db42c4be..387637f61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules/ -docs/ +docs/reference/ docker_data/ browser/bcoin* bcoin* diff --git a/docs/Beginner's-Guide.md b/docs/Beginner's-Guide.md new file mode 100644 index 000000000..90d197357 --- /dev/null +++ b/docs/Beginner's-Guide.md @@ -0,0 +1,147 @@ +## Introduction + +Bcoin is an _alternative_ implementation of the bitcoin protocol, written in node.js. It is a full node which can be used for full blockchain validation and is aware of all known consensus rules. + +## Requirements + +- Linux, OSX, or Windows (\*) (\*\*) +- node.js >=v5.0.0 +- npm >=v4.0.0 +- python2 (for node-gyp) +- gcc/g++ (for leveldb and secp256k1) +- git (optional, see below) + +(\*): Note that bcoin works best with unix-like OSes, and has not yet been thoroughly tested on windows. + +(\*\*): The BSDs and Solaris have also not been tested yet, but should work in theory. + +## Build & Install + +Bcoin is meant to be installed via npm, but for the security conscious, it may be better to clone from github. All tagged commits for release should be signed by @chjj's [PGP key][keybase] (`B4B1F62DBAC084E333F3A04A8962AB9DE6666BBD`). Signed copies of node.js are available from [nodejs.org][node], or from your respective OS's package repositories. + +### Installing via NPM + +``` bash +$ npm install -g bcoin --production +``` + +### Installing via Git + +``` bash +$ curl https://keybase.io/chjj/pgp_keys.asc | gpg --import +$ git clone git://github.com/bcoin-org/bcoin.git +$ cd bcoin +$ git tag +... +v1.0.0-alpha # latest version +$ git tag -v v1.0.0-alpha # verify signature +$ git checkout v1.0.0-alpha +$ npm install -g --production +``` + +### Troubleshooting + +If the build fails compilation for `bcoin-native` or `secp256k1-node` __validation will be slow__ (a block verification which should take 1 second on consumer grade hardware may take up to 15 seconds). Bcoin will throw a warning on boot if it detects a build failure. If you run into this issue, please post an issue on the repo. + +## Starting up your first bcoin node + +If bcoin is installed globally, `$ bcoin` should be in your PATH. If not, the bcoin bootstrap script resides in `/path/to/bcoin/bin/bcoin`. + +``` bash +$ bcoin +``` + +Will run a bcoin node as the foreground process, displaying all debug logs. + +To run as a daemon: + +``` bash +$ bcoin --daemon +``` + +This will start up a full node, complete with: a blockchain, mempool, miner, p2p server, wallet server, and an HTTP REST+RPC server. + +All logs will be written to `~/.bcoin/debug.log` by default. + +By default, the http server will only listen on `127.0.0.1:8332`. No auth will be required if an API key was not passed in. If you listen on any other host, auth will be required and an API key will be auto-generated if one was not passed in. + +## Listening Externally + +To listen publicly on the HTTP server, `--http-host=0.0.0.0` (ipv4) or `--http-host=::` (ipv4 and ipv6) can be passed. Additionally this: `--http-port=1337` can set the port. + +To advertise your node on the P2P network `--public-host=[your-public-ip]` and `--public-port=[your-public-port]` may be passed. + +## Using an API Key + +If listening publicly on the HTTP server, an API key is required. One will be generated and reported in the logs automatically if no key was chosen. An api key can be chosen with the `--api-key` option. + +Example: + +``` bash +$ bcoin --http-host=0.0.0.0 --api-key hunter2 --daemon +``` + +API keys are used with HTTP Basic Auth: + +``` bash +$ curl http://x:hunter2@localhost:8332/ +``` + +Bcoin CLI is the prepackaged tool for hitting both the REST and RPC api. + +``` bash +$ bcoin cli info --api-key hunter2 +$ bcoin rpc getblockchaininfo --api-key hunter2 +``` + +## Using Tor/SOCKS + +Bcoin has native support for SOCKS proxies, and will accept a `--proxy` option in the format of `--proxy=[user]:[pass]@host:port`. + +Passing the `--onion` option tells bcoin that the SOCKS proxy is a Tor socks proxy, and will enable Tor resolution for DNS lookups, as well as try to connect to `.onion` addresses found on the P2P network. + +``` bash +$ bcoin --proxy joe:hunter2@127.0.0.1:9050 --onion +``` + +### Running bcoin as a tor hidden service + +Your hidden service must first be configured with `tor`. Once you have the `.onion` address, it can be passed into `--public-host` in the form of `--public-host foo.onion`. + +## Target Nodes + +It's often desirable to run behind several trusted bitcoin nodes. To select permanent nodes to connect to, the `--nodes` option is available: + +``` bash +$ bcoin --nodes foo.example.com:8333,1.2.3.4:8333,5.6.7.8:8333 +``` + +If chosen, bcoin will _always_ try to connect to these nodes as outbound peers. They are top priority and whitelisted (not susceptible to permanent bans, only disconnections). + +To _only_ connect to these nodes. `--max-outbound` could be set to 3: + +``` bash +$ bcoin --nodes foo.example.com,1.2.3.4,5.6.7.8 --max-outbound 3 +``` + +## Disabling Listening + +To avoid accepting connections on the P2P network altogether, `--listen=false` can be passed to bcoin. + +### Selfish Mode + +Bcoin also supports a "selfish" mode. In this mode, bcoin still has full blockchain and mempool validation, but network services are disabled: it will not relay transactions or serve blocks to anyone. + +``` bash +$ bcoin --selfish --listen=false +``` + +Note: Selfish mode is not recommended. We encourage you to _help_ the network by relaying transactions and blocks. At the same time, selfish mode does have its uses if you do not have the bandwidth to spare, or if you're absolutely worried about potential DoS attacks. + +## Further Configuration + +See [Configuration][configuration]. + +[keybase]: https://keybase.io/chjj#show-public +[node]: https://nodejs.org/dist/v7.5.0/ +[configuration]: Configuration.md diff --git a/docs/CLI.md b/docs/CLI.md new file mode 100644 index 000000000..38d497fd0 --- /dev/null +++ b/docs/CLI.md @@ -0,0 +1,235 @@ +Bcoin ships with bcoin-cli as its default HTTP client for command line access. + +## Configuration + +Examples: + +``` bash +$ export BCOIN_API_KEY=hunter2 +$ export BCOIN_NETWORK=main +$ export BCOIN_URI=http://localhost:8332 +$ bcoin cli info +``` + +``` bash +$ bcoin cli info --api-key=hunter2 --uri=http://localhost +``` + +``` bash +$ echo 'api-key: hunter2' > ~/cli.conf +$ bcoin cli info --config=~/cli.conf +``` + +## Examples + +``` bash +$ export BCOIN_API_KEY=your-api-key + +# View the genesis block +$ bcoin cli block 0 + +# View the mempool +$ bcoin cli mempool + +# View primary wallet +$ bcoin wallet get + +# View transaction history +$ bcoin wallet history + +# Send a transaction +$ bcoin wallet send [address] 0.01 + +# View balance +$ bcoin wallet balance + +# Derive new address +$ bcoin wallet address + +# Create a new account +$ bcoin wallet account create foo + +# Send from account +$ bcoin wallet send [address] 0.01 --account=foo +``` + +RPC examples: + +``` bash +$ bcoin rpc getblockchaininfo +$ bcoin rpc getwalletinfo +$ bcoin rpc getpeerinfo +$ bcoin rpc getbalance +$ bcoin rpc listtransactions +$ bcoin rpc sendtoaddress [address] 0.01 +``` + +## Commands + +bcoin-cli commands are split into 3 categories: cli, rpc, and wallet. + +### Top-level Commands + +- `info`: Get server info. +- `wallets`: List all wallets. +- `wallet create [id]`: Create wallet. +- `broadcast [tx-hex]`: Broadcast transaction. +- `mempool`: Get mempool snapshot. +- `tx [hash/address]`: View transactions. +- `coin [hash+index/address]`: View coins. +- `block [hash/height]`: View block. +- `rescan [height]`: Rescan for transactions. +- `reset [height/hash]`: Reset chain to desired block. +- `resend`: Resend pending transactions. +- `backup [path]`: Backup the wallet db. +- `wallet [command]`: Execute wallet command. +- `rpc [command] [args]`: Execute RPC command. + +### Wallet Commands + +- `listen`: Listen for events. +- `get`: View wallet. +- `master`: View wallet master key. +- `shared add [xpubkey]`: Add key to wallet. +- `shared remove [xpubkey]`: Remove key from wallet. +- `balance`: Get wallet balance. +- `history`: View TX history. +- `pending`: View pending TXs. +- `coins`: View wallet coins. +- `account list`: List account names. +- `account create [account-name]`: Create account. +- `account get [account-name]`: Get account details. +- `address`: Derive new receiving address. +- `change`: Derive new change address. +- `nested`: Derive new nested address. +- `retoken`: Create new api key. +- `send [address] [value]`: Send transaction. +- `mktx [address] [value]`: Create transaction. +- `sign [tx-hex]`: Sign transaction. +- `zap [age?]`: Zap pending wallet TXs. +- `tx [hash]`: View transaction details. +- `blocks`: List wallet blocks. +- `block [height]`: View wallet block. +- `view [tx-hex]`: Parse and view transaction. +- `import [wif|hex]`: Import private or public key. +- `watch [address]`: Import an address. +- `key [address]`: Get wallet key by address. +- `dump [address]`: Get wallet key WIF by address. +- `lock`: Lock wallet. +- `unlock [passphrase] [timeout?]`: Unlock wallet. +- `resend`: Resend pending transactions. + +### RPC Commands + +Bcoin implements nearly all bitcoind calls along with some custom calls. + +- `stop` +- `help` +- `getblockchaininfo` +- `getbestblockhash` +- `getblockcount` +- `getblock` +- `getblockhash` +- `getblockheader` +- `getchaintips` +- `getdifficulty` +- `getmempoolancestors` +- `getmempooldescendants` +- `getmempoolentry` +- `getmempoolinfo` +- `getrawmempool` +- `gettxout` +- `gettxoutsetinfo` +- `verifychain` +- `invalidateblock` +- `reconsiderblock` +- `getnetworkhashps` +- `getmininginfo` +- `prioritisetransaction` +- `getwork` +- `getworklp` +- `getblocktemplate` +- `submitblock` +- `setgenerate` +- `getgenerate` +- `generate` +- `generatetoaddress` +- `estimatefee` +- `estimatepriority` +- `estimatesmartfee` +- `estimatesmartpriority` +- `getinfo` +- `validateaddress` +- `createmultisig` +- `createwitnessaddress` +- `verifymessage` +- `signmessagewithprivkey` +- `setmocktime` +- `getconnectioncount` +- `ping` +- `getpeerinfo` +- `addnode` +- `disconnectnode` +- `getaddednodeinfo` +- `getnettotals` +- `getnetworkinfo` +- `setban` +- `listbanned` +- `clearbanned` +- `getrawtransaction` +- `createrawtransaction` +- `decoderawtransaction` +- `decodescript` +- `sendrawtransaction` +- `signrawtransaction` +- `gettxoutproof` +- `verifytxoutproof` +- `fundrawtransaction` +- `resendwallettransactions` +- `abandontransaction` +- `addmultisigaddress` +- `addwitnessaddress` +- `backupwallet` +- `dumpprivkey` +- `dumpwallet` +- `encryptwallet` +- `getaccountaddress` +- `getaccount` +- `getaddressesbyaccount` +- `getbalance` +- `getnewaddress` +- `getrawchangeaddress` +- `getreceivedbyaccount` +- `getreceivedbyaddress` +- `gettransaction` +- `getunconfirmedbalance` +- `getwalletinfo` +- `importprivkey` +- `importwallet` +- `importaddress` +- `importprunedfunds` +- `importpubkey` +- `keypoolrefill` +- `listaccounts` +- `listaddressgroupings` +- `listlockunspent` +- `listreceivedbyaccount` +- `listreceivedbyaddress` +- `listsinceblock` +- `listtransactions` +- `listunspent` +- `lockunspent` +- `move` +- `sendfrom` +- `sendmany` +- `sendtoaddress` +- `setaccount` +- `settxfee` +- `signmessage` +- `walletlock` +- `walletpassphrasechange` +- `walletpassphrase` +- `removeprunedfunds` +- `getmemory` +- `selectwallet` +- `setloglevel` \ No newline at end of file diff --git a/docs/Configuration.md b/docs/Configuration.md new file mode 100644 index 000000000..24fca77c4 --- /dev/null +++ b/docs/Configuration.md @@ -0,0 +1,97 @@ +By default, the mainnet bcoin config file will reside in ~/.bcoin/bcoin.conf. + +All bcoin configuration options work in the config file, CLI arguments, and +process environment (with a `BCOIN_` prefix). + +## Datadir/Prefix + +Bcoin's datadir is determined by the `prefix` option. + +Example: + +``` bash +$ bcoin --prefix ~/.bcoin_spv --spv +``` + +Will create a datadir of `~/.bcoin_spv`, containing a chain database, wallet database and log file. + +## Common Options + +- `config`: Points to a custom config file, not in the prefix directory. +- `network`: Which network's chainparams to use for the node (main, testnet, regtest, or segnet4) (default: main). +- `use-workers`: Whether to use a worker process pool for transaction verification (default: true). +- `max-workers`: Number of worker processes to spawn for transaction verification. By default, the worker pool will be sized based on the number of CPUs/cores in the machine. +- `sigcache-size`: Max number of items in signature cache. + +## Node Options + +- `prefix`: The data directory (stores databases, logs, and configs) (default=~/.bcoin). +- `db`: Which database backend to use (default=leveldb). +- `max-files`: Max open files for leveldb. Higher generally means more disk page cache benefits, but also more memory usage (default: 64). +- `cache-size`: Size (in MB) of leveldb cache and write buffer (default: 32mb). + +## Logger Options + +- `log-level`: `error`, `warning`, `info`, `debug`, or `spam` (default: debug). +- `log-console`: `true` or `false` - whether to actually write to stdout/stderr + if foregrounded (default: true). +- `log-file`: Whether to use a log file (default: true). + +## Chain Options + +Note that certain chain options affect the format and indexing of the chain database and must be passed in consistently each time. + +- `prune`: Prune from the last 288 blocks (default: false). +- `checkpoints`: Use checkpoints and getheaders for the initial sync (default: true). +- `coin-cache`: The size (in MB) of the in-memory UTXO cache. By default, there is no UTXO cache enabled. To get a good number of cache hits per block, the coin cache has to be fairly large (60-100mb recommended at least). +- `index-tx`: Index transactions (enables transaction endpoints in REST api) (default: false). +- `index-address`: Index transactions and utxos by address (default: false). + +## Mempool Options + +- `mempool-size`: Max mempool size in MB (default: 100). +- `replace-by-fee`: Allow replace-by-fee transactions (default: false). +- `persistent-mempool`: Save mempool to disk and read into memory on boot (default: false). + +## Pool Options + +- `selfish`: Enable "selfish" mode (no relaying of txes or blocks) (default: false). +- `compact`: Enable compact block relay (default: true). +- `bip37`: Enable serving of bip37 merkleblocks (default: false). +- `bip151`: Enable bip151 peer-to-peer encryption (default: false). +- `listen`: Accept incoming connections (default: true). +- `max-outbound`: Max number of outbound connections (default: 8). +- `max-inbound`: Max number of inbound connections (default: 30). +- `seeds`: Custom list of DNS seeds (comma-separated). +- `host`: Host to listen on (default: 0.0.0.0). +- `port`: Port to listen on (default: 8333). +- `public-host`: Public host to advertise on network. +- `public-port`: Public port to advertise on network. +- `bip150`: Enable bip150 peer auth (default: false). +- `identity-key`: BIP150 identity key (32 byte hex string). +- `auth-peers`: Path to `authorized-peers` file for BIP150. +- `known-peers`: Path to `known-peers` file for BIP150. +- `nodes`: List of target nodes to connect to (comma-separated). + +## Miner Options + +- `coinbase-flags`: Coinbase flags (default: mined by bcoin). +- `coinbase-address`: List of payout addresses, randomly selected during block creation (comma-separated). +- `max-block-weight`: Max block weight to mine (default: 4000000). +- `reserved-block-weight`: Amount of space reserved for coinbase (default: 4000). +- `reserved-block-sigops`: Amount of sigops reserved for coinbase (default: 400). + +## HTTP + +- `http-host`: HTTP host to listen on (default: 127.0.0.1). +- `http-port`: HTTP port to listen on (default: 8332). +- `ssl-cert`: Path to SSL cert. +- `ssl-key`: Path to SSL key. +- `service-key`: Service key (used for accessing wallet system only). +- `api-key`: API key (used for accessing all node APIs). +- `wallet-auth`: Enable token auth for wallets (default: false). +- `no-auth`: Disable auth for API server and wallets (default: false). + +## Sample Config File + +See https://github.com/bcoin-org/bcoin/blob/master/etc/sample.conf. \ No newline at end of file diff --git a/docs/Design.md b/docs/Design.md new file mode 100644 index 000000000..ed02fe3f3 --- /dev/null +++ b/docs/Design.md @@ -0,0 +1,71 @@ +## Notes on Design + +Bcoin is thoroughly event driven. It has a fullnode object, but Bcoin was +specifically designed so the mempool, blockchain, p2p pool, and wallet database +could all be used separately. All the fullnode object does is tie these things +together. It's essentially a huge proxying of events. The general communication +between these things looks something like this: + +``` +pool -> block event -> chain +pool -> tx event -> mempool +chain -> block event -> mempool/miner +chain -> tx event -> walletdb +chain -> reorg event -> walletdb/mempool/miner +mempool -> tx event -> walletdb/miner +miner -> block event -> chain +walletdb -> tx event -> websocket server +websocket server -> tx event -> websocket client +http client -> tx -> http server -> mempool +``` + +Not only does the loose coupling make testing easier, it ensures people can +utilize bcoin for many use cases. + +### Performance + +Non-javscript people reading this may think using javascript isn't a wise +decision. + +#### Javascript + +Javascript is inherently slow due to how dynamic it is, but modern JITs have +solved this issue using very clever optimization and dynamic recompilation +techniques. v8 in some cases can [rival the speed of C++][v8] if the code is +well-written. + +#### Concurrency + +Bcoin runs in node.js, so the javascript code is limited to one thread. We +solve this limitation by spinning up persistent worker processes for +transaction verification (webworkers when in the browser). This ensures the +blockchain and mempool do not block the master process very much. It also means +transaction verification can be parallelized. + +Strangely enough, workers are faster in the browser than they are in node since +you are allowed to share memory between threads using the transferrable api +(Uint8Arrays can be "transferred" to another thread). In node, you have to pipe +data to another process. + +But of course, there is a benefit to having a multi-process architecture: the +worker processes can die on their own without disturbing the master process. + +Bcoin uses [secp256k1-node][secp256k1-node] for ecdsa verification, which is a +node.js binding to Pieter Wuille's blazingly fast [libsecp256k1][libsecp256k1] +library. + +In the browser, bcoin will use [elliptic][elliptic], the fastest javascript +ecdsa implementation. It will obviously never beat C and hand-optimized +assembly, but it's still usable. + +#### Benefits + +The real feature of javascript is that your code will run almost anywhere. With +bcoin, we now have a full node that will run on almost any browser, on laptops, +on servers, on smartphones, on most devices you can imagine, even by simply +visiting a webpage. + +[v8]: https://www.youtube.com/watch?v=UJPdhx5zTaw +[libsecp256k1]: https://github.com/bitcoin-core/secp256k1 +[secp256k1-node]: https://github.com/cryptocoinjs/secp256k1-node +[elliptic]: https://github.com/indutny/elliptic \ No newline at end of file diff --git a/docs/Example-Connecting-to-the-P2P-Network.md b/docs/Example-Connecting-to-the-P2P-Network.md new file mode 100644 index 000000000..c25453d91 --- /dev/null +++ b/docs/Example-Connecting-to-the-P2P-Network.md @@ -0,0 +1,86 @@ +``` js +var bcoin = require('bcoin').set('main'); + +// Create a blockchain and store it in leveldb. +// `db` also accepts `rocksdb` and `lmdb`. +var prefix = process.env.HOME + '/my-bcoin-environment'; +var chain = new bcoin.chain({ db: 'leveldb', location: prefix + '/chain' }); + +var mempool = new bcoin.mempool({ chain: chain }); + +// Create a network pool of peers with a limit of 8 peers. +var pool = new bcoin.pool({ chain: chain, mempool: mempool, maxPeers: 8 }); + +// Open the pool (implicitly opens mempool and chain). +(async function() { + await pool.open(); + + // Connect, start retrieving and relaying txs + await pool.connect(); + + // Start the blockchain sync. + pool.startSync(); + + // Watch the action + chain.on('block', function(block) { + console.log('Connected block to blockchain:'); + console.log(block); + }); + + mempool.on('tx', function(tx) { + console.log('Added tx to mempool:'); + console.log(tx); + }); + + pool.on('tx', function(tx) { + console.log('Saw transaction:'); + console.log(tx.rhash); + }); +})(); + +// Start up a segnet4 sync in-memory +// while we're at it (because we can). + +var tchain = new bcoin.chain({ + network: 'segnet4', + db: 'memory' +}); + +var tmempool = new bcoin.mempool({ + network: 'segnet4', + chain: tchain +}); + +var tpool = new bcoin.pool({ + network: 'segnet4', + chain: tchain, + mempool: tmempool, + size: 8 +}); + +(async function() { + await pool.open(); + + // Connect, start retrieving and relaying txs + await tpool.connect(); + + // Start the blockchain sync. + tpool.startSync(); + + tchain.on('block', function(block) { + console.log('Added segnet4 block:'); + console.log(block); + }); + + tmempool.on('tx', function(tx) { + console.log('Added segnet4 tx to mempool:'); + console.log(tx); + }); + + tpool.on('tx', function(tx) { + console.log('Saw segnet4 transaction:'); + console.log(tx); + }); +})(); + +``` \ No newline at end of file diff --git a/docs/Example-Creating-a-Blockchain-and-Mempool.md b/docs/Example-Creating-a-Blockchain-and-Mempool.md new file mode 100644 index 000000000..0941d9319 --- /dev/null +++ b/docs/Example-Creating-a-Blockchain-and-Mempool.md @@ -0,0 +1,35 @@ +``` js +var bcoin = require('bcoin'); + +bcoin.set({ + // Default network (so we can avoid passing + // the `network` option into every object below. + network: 'regtest', + // Enable the global worker pool + // for mining and transaction verification. + useWorkers: true +}); + +// Start up a blockchain, mempool, and miner using in-memory +// databases (stored in a red-black tree instead of on-disk). +var chain = new bcoin.chain({ db: 'memory' }); +var mempool = new bcoin.mempool({ chain: chain }); +var miner = new bcoin.miner({ chain: chain, mempool: mempool }); + +// Open the miner (initialize the databases, etc). +// Miner will implicitly call `open` on chain and mempool. +miner.open().then(function() { + // Create a Cpu miner job + return miner.createJob(); +}).then(function(job) { + // Mine the block on the worker pool (use mine() for the master process) + return job.mineAsync(); +}).then(function(block) { + // Add the block to the chain + console.log('Adding %s to the blockchain.', block.rhash); + console.log(block); + return chain.add(block); +}).then(function() { + console.log('Added block!'); +}); +``` \ No newline at end of file diff --git a/docs/Example-Fullnode-Object.md b/docs/Example-Fullnode-Object.md new file mode 100644 index 000000000..92ddf4715 --- /dev/null +++ b/docs/Example-Fullnode-Object.md @@ -0,0 +1,72 @@ +``` js +var bcoin = require('bcoin').set('main'); + +var node = bcoin.fullnode({ + checkpoints: true, + // Primary wallet passphrase + passsphrase: 'node', + logLevel: 'info' +}); + +// We get a lot of errors sometimes, +// usually from peers hanging up on us. +// Just ignore them for now. +node.on('error', function(err) { + ; +}); + +// Start the node +node.open().then(function() { + // Create a new wallet (or get an existing one with the same ID) + var options = { + id: 'mywallet', + passphrase: 'foo', + witness: false, + type: 'pubkeyhash' + }; + + return node.walletdb.create(options); +}).then(function(wallet) { + console.log('Created wallet with address: %s', wallet.getAddress('base58')); + + node.connect().then(function() { + // Start syncing the blockchain + node.startSync(); + }); + + // Wait for balance and send it to a new address. + wallet.once('balance', function(balance) { + // Create a transaction, fill + // it with coins, and sign it. + var options = { + subtractFee: true, + outputs: [{ + address: newReceiving, + value: balance.total + }] + }; + wallet.createTX(options).then(function(tx) { + // Need to pass our passphrase back in to sign! + return wallet.sign(tx, 'foo'); + }).then(function(tx) { + console.log('sending tx:'); + console.log(tx); + return node.sendTX(tx); + }).then(function() { + console.log('tx sent!'); + }); + }); +}); + +node.chain.on('block', function(block) { + ; +}); + +node.mempool.on('tx', function(tx) { + ; +}); + +node.chain.on('full', function() { + node.mempool.getHistory().then(console.log); +}); +``` \ No newline at end of file diff --git a/docs/Example-SPV-Sync.md b/docs/Example-SPV-Sync.md new file mode 100644 index 000000000..1a08fc25a --- /dev/null +++ b/docs/Example-SPV-Sync.md @@ -0,0 +1,44 @@ +``` js +var bcoin = require('bcoin').set('testnet'); + +// SPV chains only store the chain headers. +var chain = new bcoin.chain({ + db: 'leveldb', + location: process.env.HOME + '/spvchain', + spv: true +}); + +var pool = new bcoin.pool({ + chain: chain, + spv: true, + maxPeers: 8 +}); + +var walletdb = new bcoin.walletdb({ db: 'memory' }); + +pool.open().then(function() { + return walletdb.open(); +}).then(function() { + return walletdb.create(); +}).then(function(wallet) { + console.log('Created wallet with address %s', wallet.getAddress('base58')); + + // Add our address to the spv filter. + pool.watchAddress(wallet.getAddress()); + + // Connect, start retrieving and relaying txs + pool.connect().then(function() { + // Start the blockchain sync. + pool.startSync(); + + pool.on('tx', function(tx) { + walletdb.addTX(tx); + }); + + wallet.on('balance', function(balance) { + console.log('Balance updated.'); + console.log(bcoin.amount.btc(balance.unconfirmed)); + }); + }); +}); +``` \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..6c9e14677 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,42 @@ +Welcome to the bcoin docs! + +## Getting Started +- [Tutorials][tutorials] +- [Getting Started][getting-started] +- [Configuration][configuration] +- [Design][design] +- [Wallet System][wallet-system] + +## Running +- [Bcoin CLI][cli] +- [Running in the Browser][browser] +- [REST and RPC API][rest-rpc] + +## Code Examples +- [Connecting to the P2P Network][example-p2p] +- [Creating a Blockchain and Mempool][example-blockchain] +- [Fullnode Object][example-fullnode] +- [SPV Sync][example-spv] + +## Advanced +- [Working with transactions][work-transactions] +- [Scripting][scripting] + +[tutorials]: http://bcoin.io/guide-tutorial-list.html + +[getting-started]: Beginner's-Guide.md +[configuration]: Configuration.md +[design]: Design.md +[wallet-system]: Wallet-System.md + +[cli]: CLI.md +[browser]: Running-in-the-browser.md +[rest-rpc]: REST-RPC-API.md + +[work-transactions]: Working-with-transactions.md +[scripting]: Scripting.md + +[example-p2p]: Example-Connecting-to-the-P2P-Network.md +[example-blockchain]: Example-Creating-a-Blockchain-and-Mempool.md +[example-fullnode]: Example-Fullnode-Object.md +[example-spv]: Example-SPV-Sync.md diff --git a/docs/REST-RPC-API.md b/docs/REST-RPC-API.md new file mode 100644 index 000000000..a37f9f61d --- /dev/null +++ b/docs/REST-RPC-API.md @@ -0,0 +1,954 @@ +The default bcoin HTTP server listens on the standard RPC port (8332 for main). +It exposes a REST json api, as well as a JSON-RPC api. + +## Auth + +Auth is accomplished via HTTP Basic Auth, using your node's API key (passed via +`--api-key`). + +Example: + +``` bash +$ curl http://x:[api-key]@127.0.0.1:8332/ +``` + +## Errors + +Errors will be returned with an http status code other than 200, containing a +JSON object in the form: + +`{"error": { message: "message" } }` + + +## Node API + +### POST / + +Route for JSON-RPC requests, most of which mimic the bitcoind RPC calls +completely. + +Example: + +- Request Body: `{"method":"getblockchaininfo","params":[]}` +- Response Body: `{"id":...,"result":...}` + +### GET / + +Get server info. No params. + +Example: + +- Response Body: + +``` json +{ + "version": "1.0.0-alpha", + "network": "regtest", + "chain": { + "height": 177, + "tip": "18037e4e4906a6c9580356612a42bb4127888354825f4232d811ef7e89af376a", + "progress": 0.9935725241578355 + }, + "pool": { + "host": "96.82.67.198", + "port": 48444, + "agent": "/bcoin:1.0.0-alpha/", + "services": "1001", + "outbound": 1, + "inbound": 1 + }, + "mempool": { + "tx": 1, + "size": 2776 + }, + "time": { + "uptime": 6, + "system": 1486696228, + "adjusted": 1486696228, + "offset": 0 + }, + "memory": { + "rss": 63, + "jsHeap": 24, + "jsHeapTotal": 34, + "nativeHeap": 28 + } +} +``` + +### GET /coin/address/:address + +Get coins by address. Returns coins in bcoin coin json format. + +### GET /coin/:hash/:index + +Get coin by outpoint (hash and index). Returns coin in bcoin coin json format. + +### POST /coin/address + +Get coins by addresses. Returns coins in bcoin coin json format. + +Example: + +- Request Body: `{"addresses":[...]}` +- Response Body: `[...]` + +### GET /tx/:hash + +Get transaction by TXID. Returns TX in bcoin transaction json format. + +### GET /tx/address/:address + +Get transactions by address. Returns TXs in bcoin transaction json format. + +### POST /tx/address + +Get transactions by addresses. Returns TXs in bcoin transaction json format. + +Example: + +- Request Body: `{"addresses":[...]}` +- Response Body: `[...]` + +### GET /block/:block + +Get block by hash or height. + +### GET /mempool + +Get mempool snapshot (array of json txs). + +### POST /broadcast + +Broadcast a transaction by adding it to the node's mempool. If mempool +verification fails, the node will still forcefully advertise and relay the +transaction for the next 60 seconds. + +Example: + +- Request Body: `{"tx":"tx-hex"}` +- Response Body: `{"success":true}` + +### GET /fee + +Estimate smart fee. + +Example: + +- Request: GET /fee?blocks=1 +- Response Body: `{"rate":"0.0005"}` + +### POST /reset + +Reset blockchain to hash or height. The chain sync will be replayed from this height. Note that this will also rollback any wallets to the specified block. + +Example: + +- Request: POST /reset?height=100000 + + + +## Wallet API + +### Wallet Auth + +Individual wallets have their own api keys, referred to internally as "tokens" +(a 32 byte hash - calculated as `HASH256(m/44'->ec-private-key|tokenDepth)`). + +A wallet is always created with a corresponding token. When using api endpoints +for a specific wallet, the token must be sent back in the query string or json +body. + +Examples: + +``` +POST /wallet/:id/send +{ + token: "64 character hex string", + ... +} +``` + +``` +GET /wallet/:id/tx/:hash?token=[64 character hex string] +``` + +### POST /rescan + +Initiates a blockchain rescan for the walletdb. Wallets will be rolled back to the specified height (transactions above this height will be unconfirmed). + +Example: + +- Request: POST /rescan?height=100000 +- Response Body: `{"success":true}` + +### POST /resend + +Rebroadcast all pending transactions in all wallets. + +### POST /backup + +Safely backup the wallet database to specified path (creates a clone of the database). + +Example: + +- Request: POST /backup?path=/home/user/walletdb-backup.ldb +- Response Body: `{"success":true}` + +### GET /wallets + +List all wallet IDs. Returns an array of strings. + +### GET /wallet/:id + +Get wallet info by ID. + +Example: + +- Response Body: + +``` json +{ + "network": "regtest", + "wid": 1, + "id": "primary", + "initialized": true, + "watchOnly": false, + "accountDepth": 1, + "token": "2d04e217877f15ba920d02c24c6c18f4d39df92f3ae851bec37f0ade063244b2", + "tokenDepth": 0, + "state": { + "tx": 177, + "coin": 177, + "unconfirmed": "8150.0", + "confirmed": "8150.0" + }, + "master": { + "encrypted": false + }, + "account": { + "name": "default", + "initialized": true, + "witness": false, + "watchOnly": false, + "type": "pubkeyhash", + "m": 1, + "n": 1, + "accountIndex": 0, + "receiveDepth": 8, + "changeDepth": 1, + "nestedDepth": 0, + "lookahead": 10, + "receiveAddress": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS", + "nestedAddress": null, + "changeAddress": "n3nFYgQR2mrLwC3X66xHNsx4UqhS3rkSnY", + "accountKey": "tpubDC5u44zLNUVo2gPVdqCbtX644PKccH5VZB3nqUgeCiwKoi6BQZGtr5d6hhougcD6PqjszsbR3xHrQ5k8yTbUt64aSthWuNdGi7zSwfGVuxc", + "keys": [] + } +} +``` + +### GET /wallet/:id/master + +Get wallet master HD key. This is normally censored in the wallet info route. The provided api key must have _admin_ access. + +Example: + +- Response Body: + +``` json +{ + "encrypted": false, + "key": { + "xprivkey": "tprv8ZgxMBicQKsPe7977psQCjBBjWtLDoJVPiiKog42RCoShJLJATYeSkNTzdwfgpkcqwrPYAmRCeudd6kkVWrs2kH5fnTaxrHhi1TfvgxJsZD", + "mnemonic": { + "bits": 128, + "language": "english", + "entropy": "a560ac7eb5c2ed412a4ba0790f73449d", + "phrase": "pistol air cabbage high conduct party powder inject jungle knee spell derive", + "passphrase": "" + } + } +} +``` + +### PUT /wallet/:id + +Create a new wallet with a specified ID. + +Example: + +- Request: PUT /wallet/foo +- Request Body: `{"type":"pubkeyhash"}` +- Response Body: + +``` json +{ + "network": "regtest", + "wid": 2, + "id": "foo", + "initialized": true, + "watchOnly": false, + "accountDepth": 1, + "token": "d9de1ddc83bf058d14520a203df6ade0dc92a684aebfac57b667705b4cac3916", + "tokenDepth": 0, + "state": { + "tx": 0, + "coin": 0, + "unconfirmed": "0.0", + "confirmed": "0.0" + }, + "master": { + "encrypted": false + }, + "account": { + "name": "default", + "initialized": true, + "witness": false, + "watchOnly": false, + "type": "pubkeyhash", + "m": 1, + "n": 1, + "accountIndex": 0, + "receiveDepth": 1, + "changeDepth": 1, + "nestedDepth": 0, + "lookahead": 10, + "receiveAddress": "muYkrSDbD8UhyWBMXxXf99EKWn22YqmwyF", + "nestedAddress": null, + "changeAddress": "mwveV7A6svE5EGGSduZmMKTwcbE775NVFt", + "accountKey": "tpubDDh2XgSds1vBbeVgye88gsGQeCityoywRndtyrXcmvWqCgsFUyUKwzeDv8HiJhu9fC8jRAFMqxr4jj8eRTNTycmMao5wmsAScVf4jSMdPYZ", + "keys": [] + } +} +``` + +### GET /wallet/:id/account + +List all account names (array indicies map directly to bip44 account indicies). + +- Response Body: + +``` json +[ + "default" +] +``` + +### GET /wallet/:id/account/:account + +Get account info. + +Example: + +- Response Body: + +``` json +{ + "wid": 1, + "id": "primary", + "name": "default", + "initialized": true, + "witness": false, + "watchOnly": false, + "type": "pubkeyhash", + "m": 1, + "n": 1, + "accountIndex": 0, + "receiveDepth": 8, + "changeDepth": 1, + "nestedDepth": 0, + "lookahead": 10, + "receiveAddress": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS", + "nestedAddress": null, + "changeAddress": "n3nFYgQR2mrLwC3X66xHNsx4UqhS3rkSnY", + "accountKey": "tpubDC5u44zLNUVo2gPVdqCbtX644PKccH5VZB3nqUgeCiwKoi6BQZGtr5d6hhougcD6PqjszsbR3xHrQ5k8yTbUt64aSthWuNdGi7zSwfGVuxc", + "keys": [] +} +``` + +### PUT /wallet/:id/account/:name + +Create account with specified account name. + +Example: + +- Request: PUT /wallet/foo/account/my-account + +- Response Body: + +``` json +{ + "wid": 1, + "id": "primary", + "name": "menace", + "initialized": true, + "witness": false, + "watchOnly": false, + "type": "pubkeyhash", + "m": 1, + "n": 1, + "accountIndex": 1, + "receiveDepth": 1, + "changeDepth": 1, + "nestedDepth": 0, + "lookahead": 10, + "receiveAddress": "mg7b3H3ZCHx3fwvUf8gaRHwcgsL7WdJQXv", + "nestedAddress": null, + "changeAddress": "mkYtQFpxDcqutMJtyzKNFPnn97zhft56wH", + "accountKey": "tpubDC5u44zLNUVo55dtQsJRsbQgeNfrp8ctxVEdDqDQtR7ES9XG5h1SGhkv2HCuKA2RZysaFzkuy5bgxF9egvG5BJgapWwbYMU4BJ1SeSj916G", + "keys": [] +} +``` + +### POST /wallet/:id/passphrase + +Change wallet passphrase. Encrypt if unencrypted. + +Example: + +- Request Body: `{"old":"hunter2","passphrase":"hunter3"}` +- Response Body: `{"success":true}` + +### POST /wallet/:id/unlock + +Derive the AES key from passphrase and hold it in memory for a specified number +of seconds. __Note:__ During this time, account creation and signing of +transactions will _not_ require a passphrase. + +Example: + +- Request Body: `{"timeout":60,"passphrase":"hunter3"}` +- Response Body: `{"success":true}` + +### POST /wallet/:id/lock + +If `unlock` was called, zero the derived AES key and revert to normal behavior. + +### POST /wallet/:id/import + +Import a standard WIF key. Note that imported keys do not exist anywhere in the +wallet's HD tree. They can be associated with accounts but will _not_ be +properly backed up with only the mnemonic. + +A rescan will be required to see any transaction history associated with the +key. + +### POST /wallet/:id/retoken + +Derive a new wallet token, required for access of this particular wallet. + +__Note__: if you happen to lose the returned token, you _will not_ be able to +access the wallet. + +Example: + +- Response Body: + +``` json +{ + "token": "2d04e217877f15ba920d02c24c6c18f4d39df92f3ae851bec37f0ade063244b2" +} +``` + +### POST /wallet/:id/send + +Create, sign, and send a transaction. + +Example: + +- Request Body: + +``` json +{ + "rate": "0.00020", + "outputs": [{ + "value": "5.0", + "address": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS" + }] +} +``` + +- Response Body: + +``` json +{ + "wid": 1, + "id": "primary", + "hash": "0de09025e68b78e13f5543f46a9516fa37fcc06409bf03eda0e85ed34018f822", + "height": -1, + "block": null, + "ts": 0, + "ps": 1486685530, + "date": "2017-02-10T00:12:10Z", + "index": -1, + "size": 226, + "virtualSize": 226, + "fee": "0.0000454", + "rate": "0.00020088", + "confirmations": 0, + "inputs": [ + { + "value": "50.0", + "address": "n4UANJbj2ZWy1kgt9g45XFGp57FQvqR8ZJ", + "path": { + "name": "default", + "account": 0, + "change": false, + "derivation": "m/0'/0/0" + } + } + ], + "outputs": [ + { + "value": "5.0", + "address": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS", + "path": { + "name": "default", + "account": 0, + "change": false, + "derivation": "m/0'/0/7" + } + }, + { + "value": "44.9999546", + "address": "n3nFYgQR2mrLwC3X66xHNsx4UqhS3rkSnY", + "path": { + "name": "default", + "account": 0, + "change": true, + "derivation": "m/0'/1/0" + } + } + ], + "tx": "0100000001c5b23b4348b7fa801f498465e06f9e80cf2f61eead23028de14b67fa78df3716000000006b483045022100d3d4d945cdd85f0ed561ae8da549cb083ab37d82fcff5b9023f0cce608f1dffe02206fc1fd866575061dcfa3d12f691c0a2f03041bdb75a36cd72098be096ff62a810121021b018b19426faa59fdda7f57e68c42d925752454d9ea0d6feed8ac186074a4bcffffffff020065cd1d000000001976a91494bc546a84c481fbd30d34cfeeb58fd20d8a59bc88ac447b380c010000001976a914f4376876aa04f36fc71a2618878986504e40ef9c88ac00000000" +} +``` + +### POST /wallet/:id/create + +Create and template a transaction (useful for multisig). +Do not broadcast or add to wallet. + +- Request Body: + +``` json +{ + "outputs": [{ + "value": "5.0", + "address": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS" + }] +} +``` + +- Response Body: + +``` json +{ + "hash": "0799a1d3ebfd108d2578a60e1b685350d42e1ef4d5cd326f99b8bf794c81ed17", + "witnessHash": "0799a1d3ebfd108d2578a60e1b685350d42e1ef4d5cd326f99b8bf794c81ed17", + "fee": "0.0000454", + "rate": "0.00020088", + "ps": 1486686322, + "version": 1, + "flag": 1, + "inputs": [ + { + "prevout": { + "hash": "6dd8dfa9b425a4126061a1032bc6ff6e208b75ee09d0aac089d105dcf972465a", + "index": 0 + }, + "script": "483045022100e7f1d57e47cd8a28b7c27e015b291f3fd43a6eb0c051a4b65d8697b5133c29f5022020cada0f62a32aecd473f606780b2aef3fd9cbd44cfd5e9e3d9fe6eee32912df012102272dae7ff2302597cb785fd95529da6c07e32946e65ead419291258aa7b17871", + "witness": "00", + "sequence": 4294967295, + "coin": { + "version": 1, + "height": 2, + "value": "50.0", + "script": "76a9149621fb4fc6e2e48538f56928f79bef968bf17ac888ac", + "address": "muCnMvAoUFZXzuao4oy3vQJFcUntax53wE", + "coinbase": true + } + } + ], + "outputs": [ + { + "value": "5.0", + "script": "76a91494bc546a84c481fbd30d34cfeeb58fd20d8a59bc88ac", + "address": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS" + }, + { + "value": "44.9999546", + "script": "76a91458e0ea4c9722e7d079ebadb75cb3d8c16dafae7188ac", + "address": "mocuCKUU6oS6n1yyx81Au71An252SUYwDW" + } + ], + "locktime": 0 +} +``` + +### POST /wallet/:id/sign + +Sign a templated transaction (useful for multisig). + +### POST /wallet/:id/zap + +Remove all pending transactions older than a specified age. Returns array of txids. + +Example: + +- Request: POST /wallet/primary/zap?age=3600 +- Response Body: `[...]` + +### DEL /wallet/:id/tx/:hash + +Remove a pending transaction. + +### GET /wallet/:id/block + +List all block heights which contain any wallet txs. + +Example: + +- Response Body: `[1,2,3]` + +### GET /wallet/:id/block/:height + +Get block info by height. Contains a list of all wallet txs included in the +block. + +Example: + +- Request: GET /wallet/primary/block/3 +- Response Body: + +``` json +{ + "hash": "39864ce2f29635638bbdc3e943b3a182040fdceb6679fa3dabc8c827e05ff6a7", + "height": 3, + "ts": 1485471341, + "hashes": [ + "dd1a110edcdcbb3110a1cbe0a545e4b0a7813ffa5e77df691478205191dad66f" + ] +} +``` + +### PUT /wallet/:id/shared-key + +Add a shared xpubkey to wallet. Must be a `multisig` wallet. + +Example: + +- Request Body: `{"accountKey":"tpubDC5u44zLNUVo55dtQsJRsbQgeNfrp8ctxVEdDqDQtR7ES9XG5h1SGhkv2HCuKA2RZysaFzkuy5bgxF9egvG5BJgapWwbYMU4BJ1SeSj916G"}` +- Response Body: `{"success":true}` + +### DEL /wallet/:id/shared-key + +Remove shared xpubkey from wallet if present. + +### GET /wallet/:id/key/:address + +Get wallet key by address. + +### GET /wallet/:id/wif/:address + +Get wallet private key (WIF format) by address. + +Example: + +- Request: GET /wallet/primary/wif/mwX8J1CDGUqeQcJPnjNBG4s97vhQsJG7Eq +- Response Body: `"cQpRMiBcqiyjFratU2ugGEWCmGM8ctb15vhFWd74j3t4m8EzriG2"` + +### POST /wallet/:id/address + +Derive new receiving address for account. + +Example: + +- Response Body: + +``` json +{ + "network": "regtest", + "wid": 1, + "id": "primary", + "name": "default", + "account": 0, + "branch": 0, + "index": 9, + "witness": false, + "nested": false, + "publicKey": "02801d9457837ed50e9538ee1806b6598e12a3c259fdc9258bbd32934f22cb1f80", + "script": null, + "program": null, + "type": "pubkeyhash", + "address": "mwX8J1CDGUqeQcJPnjNBG4s97vhQsJG7Eq" +} +``` + +### POST /wallet/:id/change + +Derive new change address for account. + +### POST /wallet/:id/nested + +Derive new nested p2sh receiving address for account. + +### GET /wallet/:id/balance + +Get wallet or account balance. + +Example: + +- Response Body: + +``` json +{ + "wid": 1, + "id": "primary", + "account": -1, + "unconfirmed": "8149.9999546", + "confirmed": "8150.0" +} +``` + +### GET /wallet/:id/coin + +List all wallet coins available. + +### GET /wallet/:id/coin/locked + +Get all locked outpoints. + +Example: + +- Response Body: + +``` json +[{"hash":"dd1a110edcdcbb3110a1cbe0a545e4b0a7813ffa5e77df691478205191dad66f","index":0}] +``` + +### PUT /wallet/:id/coin/locked + +Lock outpoints. + +### DEL /wallet/:id/coin/locked + +Unlock outpoints. + +### GET /wallet/:id/coin/:hash/:index + +Get wallet coins. + +Example + +- Response Body: + +``` json +[ + { + "version": 1, + "height": -1, + "value": "44.9999546", + "script": "76a914f4376876aa04f36fc71a2618878986504e40ef9c88ac", + "address": "n3nFYgQR2mrLwC3X66xHNsx4UqhS3rkSnY", + "coinbase": false, + "hash": "0de09025e68b78e13f5543f46a9516fa37fcc06409bf03eda0e85ed34018f822", + "index": 1 + } +] +``` + +### GET /wallet/:id/tx/history + +Get wallet TX history. Returns array of tx details. + +### GET /wallet/:id/tx/unconfirmed + +Get pending wallet transactions. Returns array of tx details. + +### GET /wallet/:id/tx/range + +Get range of wallet transactions by timestamp. Returns array of tx details. + +Example: + +- Request: GET /wallet/primary/tx/range?start=1486695017&end=1486695359 +- Response Body: `[{tx-details}]` + +### GET /wallet/:id/tx/last + +Get last N wallet transactions. + +### GET /wallet/:id/tx/:hash + +Get wallet transaction details. + +### POST /wallet/:id/resend + +Rebroadcast all pending wallet transactions. + + + +## Wallet Events + +Wallet events use the socket.io protocol. + +Socket IO implementations: + +- JS: https://github.com/socketio/socket.io-client +- Python: https://github.com/miguelgrinberg/python-socketio +- Go: https://github.com/googollee/go-socket.io +- C++: https://github.com/socketio/socket.io-client-cpp + +### Wallet Socket Auth + +Authentication with the API server must be completed before any other events +will be accepted. + +Note that even if the server API key is disabled on the test server, the +`auth` event must still be sent to complete the handshake. + +`emit('auth', 'server-api-key')` + +The server will respond with a socket.io ACK packet once auth is completed. + +### Listening on a wallet + +After creating a websocket and authing with the server, you must send a `wallet +join` event to listen for events on a wallet. + +`emit('wallet join', 'wallet-id', 'wallet-token')` + +### Unlistening on a wallet + +`emit('wallet leave', 'wallet-id')` + +### Wallet Events + +#### `version` + +Emitted on connection. + +Returns version. Object in the form: +`[{ version: 'v1.0.0-alpha', agent: '/bcoin:v1.0.0-alpha/', network: 'main' }]`. + +#### `wallet tx` + +Received on transaction. + +Example: + +``` json +{ + "wid": 1, + "id": "primary", + "hash": "0de09025e68b78e13f5543f46a9516fa37fcc06409bf03eda0e85ed34018f822", + "height": -1, + "block": null, + "ts": 0, + "ps": 1486685530, + "date": "2017-02-10T00:12:10Z", + "index": -1, + "size": 226, + "virtualSize": 226, + "fee": "0.0000454", + "rate": "0.00020088", + "confirmations": 0, + "inputs": [ + { + "value": "50.0", + "address": "n4UANJbj2ZWy1kgt9g45XFGp57FQvqR8ZJ", + "path": { + "name": "default", + "account": 0, + "change": false, + "derivation": "m/0'/0/0" + } + } + ], + "outputs": [ + { + "value": "5.0", + "address": "mu5Puppq4Es3mibRskMwoGjoZujHCFRwGS", + "path": { + "name": "default", + "account": 0, + "change": false, + "derivation": "m/0'/0/7" + } + }, + { + "value": "44.9999546", + "address": "n3nFYgQR2mrLwC3X66xHNsx4UqhS3rkSnY", + "path": { + "name": "default", + "account": 0, + "change": true, + "derivation": "m/0'/1/0" + } + } + ], + "tx": "0100000001c5b23b4348b7fa801f498465e06f9e80cf2f61eead23028de14b67fa78df3716000000006b483045022100d3d4d945cdd85f0ed561ae8da549cb083ab37d82fcff5b9023f0cce608f1dffe02206fc1fd866575061dcfa3d12f691c0a2f03041bdb75a36cd72098be096ff62a810121021b018b19426faa59fdda7f57e68c42d925752454d9ea0d6feed8ac186074a4bcffffffff020065cd1d000000001976a91494bc546a84c481fbd30d34cfeeb58fd20d8a59bc88ac447b380c010000001976a914f4376876aa04f36fc71a2618878986504e40ef9c88ac00000000" +} +``` + +#### `wallet conflict` + +Received on double spend. + +Returns tx details of removed double spender. + +#### `wallet confirmed` + +Received when a transaction is confirmed. + +Returns tx details. + +#### `wallet unconfirmed` + +Received if a transaction was changed from +confirmed->unconfirmed as the result of a reorg. + +Returns tx details. + +#### `wallet balance` + +Received on balance update. Only emitted for +entire wallet balance (not individual accounts). + +Example: + +``` json +{ + "wid": 1, + "id": "primary", + "unconfirmed": "8149.9999546", + "confirmed": "8150.0" +} +``` + +#### `wallet address` + +Received when a new address is derived. + +Example: + +``` json +{ + "network": "regtest", + "wid": 1, + "id": "primary", + "name": "default", + "account": 0, + "branch": 0, + "index": 9, + "witness": false, + "nested": false, + "publicKey": "02801d9457837ed50e9538ee1806b6598e12a3c259fdc9258bbd32934f22cb1f80", + "script": null, + "program": null, + "type": "pubkeyhash", + "address": "mwX8J1CDGUqeQcJPnjNBG4s97vhQsJG7Eq" +} +``` \ No newline at end of file diff --git a/docs/Running-in-the-browser.md b/docs/Running-in-the-browser.md new file mode 100644 index 000000000..0e3a525d6 --- /dev/null +++ b/docs/Running-in-the-browser.md @@ -0,0 +1,16 @@ +Because bcoin is written in node.js, it is capable of being browserified. + +## Running a full node in the browser + +``` bash +$ cd ~/bcoin +$ make # Browserify bcoin +$ node browser/server.js 8080 # Start up a simple webserver and websocket->tcp bridge +$ chromium http://localhost:8080 +``` + +You should see something like this: http://i.imgur.com/0pWySyZ.png + +This is a simple proof-of-concept. It's not a pretty interface. I hope to see +others doing something far more interesting. A browser extension may be better: +the chrome extension API exposes raw TCP access. \ No newline at end of file diff --git a/docs/Scripting.md b/docs/Scripting.md new file mode 100644 index 000000000..d6c7682aa --- /dev/null +++ b/docs/Scripting.md @@ -0,0 +1,49 @@ +Scripts are array-like objects with some helper functions. + +``` js +var bcoin = require('bcoin'); +var assert = require('assert'); +var BN = bcoin.bn; +var opcodes = bcoin.script.opcodes; + +var output = new bcoin.script(); +output.push(opcodes.OP_DROP); +output.push(opcodes.OP_ADD); +output.push(new BN(7)); +output.push(opcodes.OP_NUMEQUAL); +// Compile the script to its binary representation +// (you must do this if you change something!). +output.compile(); +assert(output.getSmall(2) === 7); // compiled as OP_7 + +var input = new bcoin.script(); +input.set(0, 'hello world'); // add some metadata +input.push(new BN(2)); +input.push(new BN(5)); +input.push(input.shift()); +assert(input.getString(2) === 'hello world'); +input.compile(); + +// A stack is another array-like object which contains +// only Buffers (whereas scripts contain Opcode objects). +var stack = new bcoin.stack(); +input.execute(stack); +output.execute(stack); +// Verify the script was successful in its execution: +assert(stack.length === 1); +assert(bcoin.script.bool(stack.pop()) === true); +``` + +Using a witness would be similar, but witnesses do not get executed, they +simply _become_ the stack. The witness object itself is very similar to the +Stack object (an array-like object containing Buffers). + +``` js +var witness = new bcoin.witness(); +witness.push(new BN(2)); +witness.push(new BN(5)); +witness.push('hello world'); + +var stack = witness.toStack(); +output.execute(stack); +``` \ No newline at end of file diff --git a/docs/Wallet-System.md b/docs/Wallet-System.md new file mode 100644 index 000000000..0655c0852 --- /dev/null +++ b/docs/Wallet-System.md @@ -0,0 +1,22 @@ +Wallet REST API: [REST-RPC-API](REST-RPC-API.md) + +## Notes on wallet system + +Bcoin maintains a wallet database which contains every wallet. Wallets are _not +usable_ without also using a wallet database. For testing, the wallet database +can be in-memory, but it must be there. + +Wallets in bcoin use bip44. They also originally supported bip45 for multisig, +but support was removed to reduce code complexity, and also because bip45 +doesn't seem to add any benefit in practice. + +The wallet database can contain many different wallets, with many different +accounts, with many different addresses for each account. Bcoin should +theoretically be able to scale to hundreds of thousands of +wallets/accounts/addresses. + +Each account can be of a different type. You could have a pubkeyhash account, +as well as a multisig account, a witness pubkeyhash account, etc. + +Note that accounts should not be accessed directly from the public API. They do +not have locks which can lead to race conditions during writes. diff --git a/docs/Working-with-transactions.md b/docs/Working-with-transactions.md new file mode 100644 index 000000000..bc7365ad6 --- /dev/null +++ b/docs/Working-with-transactions.md @@ -0,0 +1,153 @@ +## TX creation + +Normal transactions in bcoin are immutable. The primary TX object contains a +bunch of consensus and policy checking methods. A lot of it is for internal use +and pretty boring for users of this library. + +Bcoin also offers a mutable transaction object (MTX). Mutable transactions +inherit from the TX object, but can also be signed and modified. + +``` js +var bcoin = require('bcoin'); +var assert = require('assert'); + +// Create an HD master keypair. +var master = bcoin.hd.generate(); + +// Derive another private hd key (we don't want to use our master key!). +var key = master.derive('m/44/0/0/0/0'); + +// Create a "keyring" object. A keyring object is basically a key manager that +// is also able to tell you info such as: your redeem script, your scripthash, +// your program hash, your pubkey hash, your scripthash program hash, etc. +// In this case, we'll make it simple and just add one key for a +// pubkeyhash address. `getPublicKey` returns the non-hd public key. +var keyring = new bcoin.keyring(key.privateKey); + +console.log(keyring.getAddress()); + +// Create a fake coinbase for our funding. +var cb = new bcoin.mtx(); + +// Add a typical coinbase input +cb.addInput({ + prevout: new bcoin.outpoint(), + script: new bcoin.script(), + sequence: 0xffffffff +}); + +// Send 50,000 satoshis to ourself. +cb.addOutput({ + address: keyring.getAddress(), + value: 50000 +}); + +// Create our redeeming transaction. +var mtx = new bcoin.mtx(); + +// Add output 0 from our coinbase as an input. +mtx.addTX(cb, 0); + +// Send 10,000 satoshis to ourself, +// creating a fee of 40,000 satoshis. +mtx.addOutput({ + address: keyring.getAddress(), + value: 10000 +}); + +// Sign input 0: pass in our keyring. +mtx.sign(keyring); + +// The transaction should now verify. +assert(mtx.verify()); +assert(mtx.getFee() === 40000); + +// Commit our transaction and make it immutable. +// This turns it from an MTX into a TX object. +var tx = mtx.toTX(); + +// The transaction should still verify. +// Regular transactions require a coin +// viewpoint to be passed in. +assert(tx.verify(mtx.view)); +assert(tx.getFee(mtx.view) === 40000); +``` + +### Coin Selection + +The above method works, but is pretty contrived. In reality, you probably +wouldn't select inputs and calculate the fee by hand. You would want a +change output added. Bcoin has a nice method of dealing with this. + +Let's try it more realistically: + +``` js +var bcoin = require('bcoin'); +var assert = require('assert'); + +var master = bcoin.hd.generate(); +var key = master.derive('m/44/0/0/0/0'); +var keyring = new bcoin.keyring(key.privateKey); +var cb = new bcoin.mtx(); + +cb.addInput({ + prevout: new bcoin.outpoint(), + script: new bcoin.script(), + sequence: 0xffffffff +}); + +// Send 50,000 satoshis to ourselves. +cb.addOutput({ + address: keyring.getAddress(), + value: 50000 +}); + +// Our available coins. +var coins = []; + +// Convert the coinbase output to a Coin +// object and add it to our available coins. +// In reality you might get these coins from a wallet. +var coin = bcoin.coin.fromTX(cb, 0, -1); +coins.push(coin); + +// Create our redeeming transaction. +var mtx = new bcoin.mtx(); + +// Send 10,000 satoshis to ourself. +mtx.addOutput({ + address: keyring.getAddress(), + value: 10000 +}); + +// Now that we've created the output, we can do some coin selection (the output +// must be added first so we know how much money is needed and also so we can +// accurately estimate the size for fee calculation). + +// Select coins from our array and add inputs. +// Calculate fee and add a change output. +mtx.fund(coins, { + // Use a rate of 10,000 satoshis per kb. + // With the `fullnode` object, you can + // use the fee estimator for this instead + // of blindly guessing. + rate: 10000, + // Send the change back to ourselves. + changeAddress: keyring.getAddress() +}).then(function() { + // Sign input 0 + mtx.sign(keyring); + + // The transaction should now verify. + assert(mtx.verify()); + + // Commit our transaction and make it immutable. + // This turns it from an MTX into a TX. + var tx = mtx.toTX(); + + // The transaction should still verify. + // Regular transactions require a coin + // viewpoint to be passed in. + assert(tx.verify(mtx.view)); +}); +``` \ No newline at end of file diff --git a/jsdoc.json b/jsdoc.json index ad6db37d8..ecc67ec95 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -16,7 +16,7 @@ "opts": { "template": "templates/default", "encoding": "utf8", - "destination": "./docs/", + "destination": "./docs/reference", "recurse": true, "private": true, "pedantic": true