Skip to content

Commit

Permalink
Use btcd (#50)
Browse files Browse the repository at this point in the history
* Add btcd to the build process

* Fixes

* Add certs

* Fix types

* Get rid of buildConstants. It wasn't worth it.

* Remove some Mempool value unsupported by btcd

* Generate secure btcd RPC credentials and allow testnet through command line args

* Handle waiting for btcd initialization on the UI

* Disable Bcore-only code

* Update README.md and SECURITY.md

* Check-in windows binary

* Fix Settings.test.ts

* Fix other tests by mocking FeatureFlags and enabling Bcore in the tests

* Test AwaitBtcd

* Address PR review issues

* Fix some TypeScript and btcd compatibility issues

* Increase Jest tests timeout

* Fix executable's name on Windows

* Fix executable's name on Windows (take 2)

* Fix executable's name on Windows (take 3)
  • Loading branch information
msafi authored May 26, 2020
1 parent b0a8199 commit 41c1940
Show file tree
Hide file tree
Showing 40 changed files with 506 additions and 239 deletions.
5 changes: 2 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
// "program": "${workspaceRoot}/src/main/main.ts",
"outFiles": ["${workspaceRoot}/artifacts/webpack/main/main.js"],
"sourceMaps": true,
"args": ["./artifacts/webpack"],
// "args": ["."],
// "args": ["./artifacts/webpack"],
"args": ["./artifacts/webpack", "--args", "--testnet"],
"outputCapture": "std",
"preLaunchTask": "_webpack-build-main",
"env": {
Expand Down
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
"coverage": true
},
"cSpell.words": [
"Bcore",
"Erroring",
"Executables",
"Mempool",
"SCROLLABLE",
"Satoshi",
"bitcoind",
"btcd",
"clsx",
"formik",
"start",
"testid"
],
"editor.formatOnSave": true,
Expand Down
119 changes: 11 additions & 108 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,132 +2,35 @@

![Master](https://github.com/orange-org/orange/workflows/Master/badge.svg)

Orange is a Bitcoin blockchain explorer for Bitcoin Core. It's built with
Electron, TypeScript and React.
Orange aims to be a mainstream user-friendly Bitcoin payment software. The
project is under active development.

This project is not affiliated with Bitcoin Core.
Orange is built with Electron, TypeScript and React. It uses
[btcd](https://github.com/btcsuite/btcd) as its back-end.

![Orange](./docs/orange.png)

## Table of Contents

- [Goal of the project](#goal-of-the-project)
- [How it works](#how-it-works)
- [Architecture and security](#architecture-and-security)
- [Install and contribute](#install-and-contribute)
- [Questions and help](#questions-and-help)

## Goal of the project

The goal of the project is to explore using Electron, React, and TypeScript to
build a better Bitcoin client on top of Bitcoin Core while still providing
strong security.
The goal is to be a mainstream payment software for merchants, power-users, and
Bitcoin enthusiasts, to deliver an integrated package of a full-node, on-chain,
and off-chain payments, with best privacy and security options as the default.
Provide a simple and modern interface to send and receive payments, free of
technical jargon and overwhelming configuration options.

The initial aim of Orange is to be a graphical blockchain explorer. Orange may
gradually include more features, such as wallet.

## How it works

Orange is just a front-end. Bitcoin Core acts as the back-end. Orange needs
Bitcoin Core to be already running on your computer.

## Architecture and security

Orange uses multiple processes. Some processes include npm modules while others
don't. Orange is architected so that processes with npm modules are sandboxed
and have very low access privileges. Processes with npm modules cannot make
outbound or receive inbound connections except in a very tightly controlled
manner.

Only processes that don't use any 3rd party modules are allowed to communicate
with Bitcoin Core.

### Details on the architecture

All Electron applications have 3 separate processes. The nature of these 3
processes is what enables the architecture described above.

The 3 processes are called `main`, `renderer`, and `preload`. Each one of these
processes is granted a different level of access privilege over the system, as
described below.

#### The `main` process

In Orange the `main` has full access over the system. It uses Node.js to talk to
the file system and it can talk to the operating system. **Because `main` has
this much privilege, we don't use npm modules in it.**

`main` talks to Bitcoin Core.

#### The `renderer` process

The `renderer` process is where the UI code is.

The `renderer` process has no access to Node.js APIs, the filesystem, or any
operating system features. The `renderer` process is also prohibited from:

- making network requests
- loading remote content (at run time)
- opening webpages
- navigating

<details><summary>Some implementation details</summary>

We implement the
[security recommendations](https://electronjs.org/docs/tutorial/security?q=j#checklist-security-recommendations)
provided by Electron. Many of these recommendations are particular to loading
"remote content", that is content over the network. In Orange we disable
networking completely, but we consider npm modules in the `renderer` process to
be equivalent to "remote content" so we follow these recommendations as strictly
as possible:

- Node integration is disabled
- Content isolation is enabled
- Web security is enabled
- A strict content security policy is provided
- Running insecure content is disabled
- No experimental Chromium or Blink features are used
- WebView creation is disabled
- Navigation is disabled
- The remote module is disabled

</details>

#### How does `renderer` get the data to display if it's sandboxed?

This is where the `preload` process comes in. `preload` is the middleman between
`main` and `renderer`. It relays messages between the two, but only very
specific kinds of messages.

#### How is the communication between `renderer` and `main` secured?

`main` and `renderer` use a nonce (i.e. password) to communicate with each
other. This nonce is agreed upon between `main` and `renderer` only after all
the npm modules have been downloaded, so remote code has no way of knowing what
it is.

<details><summary>Implementation details</summary>

After the npm modules have been downloaded but before the Orange distributable
is created, the string `__NONCE__` in the code will be replaced with a base64
encoded random bytes. Care has to be taken to make sure this nonce is only known
to the local Orange code, not to the npm modules.

</details>
And for all of this to be built on a robust and
[secure code and architecture](./SECURITY.md).

## Install and contribute

Orange development was only tested on macOS. It should work on other operating
systems but I haven't tested it. Please go ahead and test it and report any
issues.

To run this locally and contribute:

1. Have Bitcoin Core running
1. Have `server=1` and `prune=0` in your `bitcoin.conf` file (otherwise Orange
won't work)
1. Have Bitcoin Core `datadir` location set to default (otherwise Orange won't
be able to authenticate with Bitcoin Core)
1. Clone this repo
1. `cd` into the repo
1. Execute `npm install` to install the dependencies
Expand Down
83 changes: 83 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Security and Architecture

Orange uses multiple processes. Some processes include npm modules while others
don't. Orange is architected so that processes with npm modules are sandboxed
and have very low access privileges. Processes with npm modules cannot make
outbound or receive inbound connections except in a very tightly controlled
manner.

Only processes that don't use any 3rd party modules are allowed to communicate
with Bitcoin Core.

## Details on the architecture

All Electron applications have 3 separate processes. The nature of these 3
processes is what enables the architecture described above.

The 3 processes are called `main`, `renderer`, and `preload`. Each one of these
processes is granted a different level of access privilege over the system, as
described below.

### The `main` process

In Orange the `main` has full access over the system. It uses Node.js to talk to
the file system and it can talk to the operating system. **Because `main` has
this much privilege, we don't use npm modules in it.**

`main` talks to Bitcoin Core.

### The `renderer` process

The `renderer` process is where the UI code is.

The `renderer` process has no access to Node.js APIs, the filesystem, or any
operating system features. The `renderer` process is also prohibited from:

- making network requests
- loading remote content (at run time)
- opening webpages
- navigating

<details><summary>Some implementation details</summary>

We implement the
[security recommendations](https://electronjs.org/docs/tutorial/security?q=j#checklist-security-recommendations)
provided by Electron. Many of these recommendations are particular to loading
"remote content", that is content over the network. In Orange we disable
networking completely, but we consider npm modules in the `renderer` process to
be equivalent to "remote content" so we follow these recommendations as strictly
as possible:

- Node integration is disabled
- Content isolation is enabled
- Web security is enabled
- A strict content security policy is provided
- Running insecure content is disabled
- No experimental Chromium or Blink features are used
- WebView creation is disabled
- Navigation is disabled
- The remote module is disabled

</details>

### How does `renderer` get the data to display if it's sandboxed?

This is where the `preload` process comes in. `preload` is the middleman between
`main` and `renderer`. It relays messages between the two, but only very
specific kinds of messages.

### How is the communication between `renderer` and `main` secured?

`main` and `renderer` use a nonce (i.e. password) to communicate with each
other. This nonce is agreed upon between `main` and `renderer` only after all
the npm modules have been downloaded, so remote code has no way of knowing what
it is.

<details><summary>Implementation details</summary>

After the npm modules have been downloaded but before the Orange distributable
is created, the string `__NONCE__` in the code will be replaced with a base64
encoded random bytes. Care has to be taken to make sure this nonce is only known
to the local Orange code, not to the npm modules.

</details>
3 changes: 3 additions & 0 deletions __mocks__/child_process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const spawn = () => ({
on: () => null,
});
8 changes: 7 additions & 1 deletion jest/setupFileAfterEnv.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ window.HTMLElement.prototype.scrollIntoView = () => null;

jest.useFakeTimers(); // Tests are a not the place to have real setTimeouts and setIntervals

/**
* Increase Jest tests timeout from 5 to 8 seconds because sometimes they do
* take longer
*/
jest.setTimeout(8000);

require("@testing-library/jest-dom/extend-expect");

jest.mock("fs");
// jest.mock("_r/rpcClient/rpcClient");
jest.mock("child_process");
jest.mock("_m/installExtensions");
jest.mock("_m/getGlobalProcess", () => ({
getGlobalProcess: jest.fn(),
Expand Down
Binary file added src/bin/darwin-x64/btcd
Binary file not shown.
Binary file added src/bin/win32-x64/btcd.exe
Binary file not shown.
6 changes: 5 additions & 1 deletion src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { featureFlags } from "_f/featureFlags";

export const BITCOIN_CORE_RPC_ERROR = {
/**
* The below codes map to:
Expand Down Expand Up @@ -25,4 +27,6 @@ export const ERROR = {
general: 5001,
} as const;

export const DEFAULT_SERVER_URL = "http://localhost:8332";
export const DEFAULT_SERVER_URL = featureFlags.useBcore
? "http://127.0.0.1:8332"
: "http://127.0.0.1:8334";
3 changes: 3 additions & 0 deletions src/featureFlags/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const featureFlags = {
useBcore: false,
};
26 changes: 26 additions & 0 deletions src/main/commandLineArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getGlobalProcess } from "./getGlobalProcess";

export type Arguments = {
datadir?: string;
testnet?: string;
};

const parseCommandLineArgs = () => {
const globalProcess = getGlobalProcess();
const args = globalProcess.argv;
const argsObj = args.reduce<Arguments>((obj, arg) => {
const [name, value] = arg.split("=");

/* istanbul ignore if */
if (name.substr(0, 2) === "--") {
// eslint-disable-next-line no-param-reassign
obj[name.substr(2) as keyof Arguments] = value || "true";
}

return obj;
}, {});

return argsObj;
};

export const commandLineArgs = parseCommandLineArgs();
21 changes: 21 additions & 0 deletions src/main/mainRpcClient/getBtcdRpcConfigurations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { randomBytes } from "crypto";
import { commandLineArgs } from "_m/commandLineArgs";

const username = randomBytes(16).toString("hex");
const password = randomBytes(16).toString("hex");
export const hostname = "127.0.0.1";

export const getBtcdRpcConfigurations = () => {
let port = 8334;

/* istanbul ignore if */
if (commandLineArgs.testnet) {
port = 18334;
}

return {
username,
password,
serverUrl: `http://${hostname}:${port}`,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export const getServerUrl = (chainName?: string) => {
? /* istanbul ignore next */ 18443
: 8332;

return `http://localhost:${port}`;
return `http://127.0.0.1:${port}`;
};
Loading

0 comments on commit 41c1940

Please sign in to comment.