Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve client and deprecate useless features #149

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
version: '2.6.1'

- name: Start containers
run: docker-compose up -d --build --wait
run: docker-compose up -d --build

- name: Install dependencies
run: yarn
Expand Down
126 changes: 9 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,59 +23,16 @@ npm install bitcoin-core --save
## Usage
### Client(...args)
#### Arguments
1. `[agentOptions]` _(Object)_: Optional `agent` [options](https://github.com/request/request#using-optionsagentoptions) to configure SSL/TLS.
2. `[headers=false]` _(boolean)_: Whether to return the response headers.
3. `[host=localhost]` _(string)_: The host to connect to.
4. `[logger=debugnyan('bitcoin-core')]` _(Function)_: Custom logger (by default, `debugnyan`).
5. `[network=mainnet]` _(string)_: The network
6. `[password]` _(string)_: The RPC server user password.
7. `[port=[network]]` _(string)_: The RPC server port.
8. `[ssl]` _(boolean|Object)_: Whether to use SSL/TLS with strict checking (_boolean_) or an expanded config (_Object_).
9. `[ssl.enabled]` _(boolean)_: Whether to use SSL/TLS.
10. `[ssl.strict]` _(boolean)_: Whether to do strict SSL/TLS checking (certificate must match host).
11. `[timeout=30000]` _(number)_: How long until the request times out (ms).
12. `[username]` _(number)_: The RPC server user name.
13. `[version]` _(string)_: Which version to check methods for ([read more](#versionchecking)).
14. `[wallet]` _(string)_: Which wallet to manage ([read more](#multiwallet)).
1. `[headers={}]` _(object)_: Custom request headers.
2. `[host=http://localhost:8332]` _(string)_: The host to connect to.
3. `[logger=debugnyan('bitcoin-core')]` _(Function)_: Custom logger (by default, `debugnyan`).
4. `[password]` _(string)_: The RPC server user password.
5. `[timeout=30000]` _(number)_: How long until the request times out (ms).
6. `[username]` _(number)_: The RPC server user name.
7. `[version]` _(string)_: Which version to check methods for ([read more](#versionchecking)).
8. `[wallet]` _(string)_: Which wallet to manage ([read more](#multiwallet)).

### Examples
#### Using network mode
The `network` will automatically determine the port to connect to, just like the `bitcoind` and `bitcoin-cli` commands.

```js
const Client = require('bitcoin-core');
const client = new Client({ network: 'regtest' });
```

##### Setting a custom port

```js
const client = new Client({ port: 28332 });
```

#### Connecting to an SSL/TLS server with strict checking enabled
By default, when `ssl` is enabled, strict checking is implicitly enabled.

```js
const fs = require('fs');
const client = new Client({
agentOptions: {
ca: fs.readFileSync('/etc/ssl/bitcoind/cert.pem')
},
ssl: true
});
```

#### Connecting to an SSL/TLS server without strict checking enabled

```js
const client = new Client({
ssl: {
enabled: true,
strict: false
}
});
```

#### Using promises to process the response

Expand All @@ -91,19 +48,6 @@ Callback support was removed. Since every method returns a `Promise`, [callbacki
util.callbackify(() => client.getInfo())((error, help) => console.log(help));
```

#### Returning headers in the response
For compatibility with other Bitcoin Core clients.

```js
const client = new Client({ headers: true });

// Promise style with headers enabled:
client.getInfo().then(([body, headers]) => console.log(body, headers));

// Await style based on promises with headers enabled:
const [body, headers] = await client.getInfo();
```

## Named parameters

Since version v0.14.0, it is possible to send commands via the JSON-RPC interface using named parameters instead of positional ones. This comes with the advantage of making the order of arguments irrelevant. It also helps improving the readability of certain function calls when leaving out arguments for their default value.
Expand Down Expand Up @@ -223,8 +167,6 @@ docker run --rm -it ruimarinho/bitcoin-core:0.12-alpine -printtoconsole -rpcuser

These configuration values may also be set on the `bitcoin.conf` file of your platform installation.

By default, port `8332` is used to listen for requests in `mainnet` mode, or `18332` in `testnet` and `regtest` modes (the regtest change will be changed to `18443` in [0.16](https://github.com/bitcoin/bitcoin/pull/10825)). Use the `network` property to initialize the client on the desired mode and automatically set the respective default port. You can optionally set a custom port of your choice too.

The RPC services binds to the localhost loopback network interface, so use `rpcbind` to change where to bind to and `rpcallowip` to whitelist source IP access.

#### Methods
Expand Down Expand Up @@ -386,58 +328,8 @@ client.getUnspentTransactionOutputs([{
}], { extension: 'json' })
```

### SSL
This client supports SSL out of the box. Simply pass the SSL public certificate to the client and optionally disable strict SSL checking which will bypass SSL validation (the connection is still encrypted but the server it is connecting to may not be trusted). This is, of course, discouraged unless for testing purposes when using something like self-signed certificates.

#### Generating a self-signed certificates for testing purposes
Please note that the following procedure should only be used for testing purposes.

Generate an self-signed certificate together with an unprotected private key:

```sh
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650 -nodes
```

#### Connecting via SSL
On Bitcoin Core <0.12, you can start the `bitcoind` RPC server directly with SSL:

```sh
docker run --rm -it -v $(PWD)/ssl:/etc/ssl ruimarinho/bitcoin-core:0.11-alpine -printtoconsole -rpcuser=foo -rpcpassword=bar -rpcssl -rpcsslcertificatechainfile=/etc/ssl/bitcoind/cert.pem -rpcsslprivatekeyfile=/etc/ssl/bitcoind/key.pem -server
```

On Bitcoin Core >0.12, use must use `stunnel` (`brew install stunnel` or `sudo apt-get install stunnel4`) or an HTTPS reverse proxy to configure SSL since the built-in support for SSL has been removed. The trade off with `stunnel` is performance and simplicity versus features, as it lacks more powerful capacities such as Basic Authentication and caching which are standard in reverse proxies.

You can use `stunnel` by configuring `stunnel.conf` with the following service requirements:

```
[bitcoin]
accept = 28332
connect = 18332
cert = /etc/ssl/bitcoind/cert.pem
key = /etc/ssl/bitcoind/key.pem
```

The `key` option may be omitted if you concatenating your private and public certificates into a single `stunnel.pem` file.

On some versions of `stunnel` it is also possible to start a service using command line arguments. The equivalent would be:

```sh
stunnel -d 28332 -r 127.0.0.1:18332 -p stunnel.pem -P ''
```

Then pass the public certificate to the client:

```js
const Client = require('bitcoin-core');
const fs = require('fs');
const client = new Client({
agentOptions: {
ca: fs.readFileSync('/etc/ssl/bitcoind/cert.pem')
},
port: 28332,
ssl: true
});
```
Deprecated since release 0.5.0.

## Logging

Expand Down
76 changes: 12 additions & 64 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,89 +1,37 @@
version: '3.4'

services:
bitcoin-core:
image: ruimarinho/bitcoin-core:0.17
image: bitcoin/bitcoin:28.0
command:
-fallbackfee=0.0002
-printtoconsole
-regtest=1
-rest
-rpcallowip=::/0
-rpcallowip=0.0.0.0/0
-rpcbind=0.0.0.0
-rpcpassword=bar
-rpcport=18443
-rpcuser=foo
-txindex=1
-server
ports:
- 18443:18443
healthcheck:
test: curl --fail "http://foo@localhost:18443/rest/chaininfo.json" || exit 1
interval: 2s
retries: 100
start_period: 2s
timeout: 2s
- 18443:18443

bitcoin-core-multi-wallet:
image: ruimarinho/bitcoin-core:0.17
image: bitcoin/bitcoin:28.0
command:
-fallbackfee=0.0002
-printtoconsole
-regtest=1
-rest
-rpcallowip=::/0
-rpcallowip=0.0.0.0/0
-rpcbind=0.0.0.0
-rpcpassword=bar
-wallet
-wallet=wallet
-wallet=wallet1
-wallet=wallet2
-rpcport=18443
-rpcuser=foo
-txindex=1
-server
ports:
- 18453:18443
healthcheck:
test: curl --fail "http://foo@localhost:18443/rest/chaininfo.json" || exit 1
interval: 2s
retries: 100
start_period: 2s
timeout: 2s

bitcoin-core-ssl:
image: ruimarinho/bitcoin-core:0.11
command:
-printtoconsole
-regtest=1
-rest
-rpcallowip=::/0
-rpcpassword=bar
-rpcport=18443
-rpcssl
-rpcsslcertificatechainfile=/etc/ssl/bitcoind/cert.pem
-rpcsslprivatekeyfile=/etc/ssl/bitcoind/key.pem
-rpcuser=foo
-server
volumes:
- ./test/config/ssl:/etc/ssl/bitcoind
ports:
- 18463:18443
healthcheck:
test: curl --fail -k "https://foo@localhost:18443/rest/chaininfo.json" || exit 1
interval: 2s
retries: 100
start_period: 2s
timeout: 2s

bitcoin-core-username-only:
image: ruimarinho/bitcoin-core:0.11
command:
-printtoconsole
-regtest=1
-rest
-rpcallowip=::/0
-rpcport=18443
-rpcuser=foo
-server
ports:
- 18473:18443
healthcheck:
test: curl --fail "http://foo@localhost:18443/rest/chaininfo.json" || exit 1
interval: 2s
retries: 100
start_period: 2s
timeout: 2s
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"changelog": "github_changelog_generator --project bitcoin-core --user ruimarinho --no-issues --header-label='# Changelog' --future-release=$npm_config_future_release && sed -i '' -e :a -e '$d;N;2,4ba' -e 'P;D' CHANGELOG.md",
"cover": "nyc --reporter=html --reporter=text npm test",
"dependencies": "docker-compose up -d bitcoind bitcoind-ssl bitcoind-username-only",
"dependencies": "docker-compose up -d bitcoind bitcoind-username-only",
"lint": "eslint src test",
"test": "NODE_ENV=test mocha $npm_package_options_mocha",
"testdocker": "docker-compose run --rm sut",
Expand Down
112 changes: 112 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
declare module 'bitcoin-core' {

interface Auth {
pass?: string;
user?: string;
}

interface MethodFeature {
supported: boolean;
}

interface Method {
features: Record<string, MethodFeature>;
supported: boolean;
}

interface RequesterConfig {
methods: Record<string, Method>;
version?: string;
}

interface Parser {
rpc(response: any): any;
rest(extension: string, response: any): any;
}

interface RequestConfig {
baseUrl: string;
headers: Record<string, string>;
timeout: number;
}

interface Request {
getAsync(url: string, options?: Record<string, any>): Promise<any>;
postAsync(url: string, options?: Record<string, any>): Promise<any>;
defaults(config: RequestConfig): Request;
}

interface ClientConfig {
headers?: Record<string, string>;
host?: string;
logger?: any;
password?: string;
timeout?: number;
username?: string;
version?: string;
wallet?: string;
allowDefaultWallet?: boolean;
}

interface ReturnFormatOptions {
extension?: 'json' | 'bin' | 'hex';
}

interface BlockHashOptions extends ReturnFormatOptions {
summary?: boolean;
}

interface UnspentTransactionOutput {
id: string;
index: number;
}

class Client {
auth: Auth | null;
hasNamedParametersSupport: boolean;
headers: Record<string, string>;
host: string;
password?: string;
timeout: number;
wallet?: string;
version?: string;

methods: Record<string, Method>;
request: Request;
requester: any; // Assume Requester type
parser: Parser;

constructor(config?: ClientConfig);

command<T = any>(...args: any[]): Promise<T>;

getTransactionByHash<T = any>(
hash: string,
options?: ReturnFormatOptions
): Promise<T>;

getBlockByHash<T = any>(
hash: string,
options?: BlockHashOptions
): Promise<T>;

getBlockHeadersByHash<T = any>(
hash: string,
count: number,
options?: ReturnFormatOptions
): Promise<T>;

getBlockchainInformation<T = any>(): Promise<T>;

getUnspentTransactionOutputs<T = any>(
outpoints: UnspentTransactionOutput[],
options?: ReturnFormatOptions
): Promise<T>;

getMemoryPoolContent<T = any>(): Promise<T>;

getMemoryPoolInformation<T = any>(): Promise<T>;
}

export = Client;
}
Loading
Loading