diff --git a/.gitignore b/.gitignore index b8c785e..29e02a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ electrs.s9pk image.tar +scripts/embassy.js +.vscode diff --git a/Makefile b/Makefile index 4e446bf..4b59e0a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,8 @@ -ASSETS := $(shell yq e '.assets.[].src' manifest.yaml) -ASSET_PATHS := $(addprefix assets/,$(ASSETS)) VERSION := $(shell yq e ".version" manifest.yaml) ELECTRS_SRC := $(shell find ./electrs/src) electrs/Cargo.toml electrs/Cargo.lock CONFIGURATOR_SRC := $(shell find ./configurator/src) configurator/Cargo.toml configurator/Cargo.lock S9PK_PATH=$(shell find . -name electrs.s9pk -print) +TS_FILES := $(shell find . -name \*.ts ) .DELETE_ON_ERROR: @@ -12,11 +11,14 @@ all: verify verify: electrs.s9pk $(S9PK_PATH) embassy-sdk verify s9pk $(S9PK_PATH) +install: all electrs.s9pk + embassy-cli package install electrs.s9pk + clean: rm -f image.tar rm -f electrs.s9pk -electrs.s9pk: manifest.yaml assets/compat/* image.tar instructions.md $(ASSET_PATHS) +electrs.s9pk: manifest.yaml image.tar instructions.md scripts/embassy.js embassy-sdk pack image.tar: Dockerfile check*.sh docker_entrypoint.sh configurator/target/aarch64-unknown-linux-musl/release/configurator $(ELECTRS_SRC) @@ -24,3 +26,6 @@ image.tar: Dockerfile check*.sh docker_entrypoint.sh configurator/target/aarch64 configurator/target/aarch64-unknown-linux-musl/release/configurator: $(CONFIGURATOR_SRC) docker run --rm -it -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)"/configurator:/home/rust/src start9/rust-musl-cross:aarch64-musl cargo build --release + +scripts/embassy.js: $(TS_FILES) + deno bundle scripts/embassy.ts scripts/embassy.js diff --git a/assets/compat/bitcoind.rules.yaml b/assets/compat/bitcoind.rules.yaml deleted file mode 100644 index 0cc52a7..0000000 --- a/assets/compat/bitcoind.rules.yaml +++ /dev/null @@ -1,18 +0,0 @@ -- rule: "rpc.enable?" - description: "Must have RPC interface enabled" - suggestions: - - SET: - var: "rpc.enable" - to-value: true -- rule: "advanced.peers.listen?" - description: "Must have peer interface enabled" - suggestions: - - SET: - var: "advanced.peers.listen" - to-value: true -- rule: '''advanced.pruning.mode = "disabled"' - description: Pruning must be disabled (ie. must be a full archival node) - suggestions: - - SET: - var: 'advanced.pruning.mode' - to-value: disabled diff --git a/assets/compat/btc-rpc-proxy.rules.yaml b/assets/compat/btc-rpc-proxy.rules.yaml deleted file mode 100644 index 78b0777..0000000 --- a/assets/compat/btc-rpc-proxy.rules.yaml +++ /dev/null @@ -1,62 +0,0 @@ -- rule: '''users.*.name = "electrs"' - description: 'Must have an RPC user named "electrs"' - suggestions: - - PUSH: - to: "users" - value: - name: electrs - allowed-calls: [] - fetch-blocks: false - - SET: - var: 'users.[first(item => ''item.name = "electrs")].password' - to-entropy: - charset: "a-z,A-Z,0-9" - len: 22 -- rule: '!(users.[first(item => ''item.name = "electrs")].fetch-blocks?)' - description: 'RPC user "electrs" must have "Fetch Blocks" disabled' - suggestions: - - SET: - var: 'users.[first(item => ''item.name = "electrs")].fetch-blocks' - to-value: false -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "estimatesmartfee"' - description: 'RPC user "electrs" must have "estimatesmartfee" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "estimatesmartfee" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getblockchaininfo"' - description: 'RPC user "electrs" must have "getblockchaininfo" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getblockchaininfo" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getblockcount"' - description: 'RPC user "electrs" must have "getblockcount" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getblockcount" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getmempoolentry"' - description: 'RPC user "electrs" must have "getmempoolentry" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getmempoolentry" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getnetworkinfo"' - description: 'RPC user "electrs" must have "getnetworkinfo" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getnetworkinfo" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getrawmempool"' - description: 'RPC user "electrs" must have "getrawmempool" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getrawmempool" -- rule: '''users.[first(item => ''item.name = "electrs")].allowed-calls.* = "getrawtransaction"' - description: 'RPC user "electrs" must have "getrawtransaction" enabled' - suggestions: - - PUSH: - to: 'users.[first(item => ''item.name = "electrs")].allowed-calls' - value: "getrawtransaction" diff --git a/assets/compat/config_rules.yaml b/assets/compat/config_rules.yaml deleted file mode 100644 index fe51488..0000000 --- a/assets/compat/config_rules.yaml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/assets/compat/config_spec.yaml b/assets/compat/config_spec.yaml deleted file mode 100644 index bc98e03..0000000 --- a/assets/compat/config_spec.yaml +++ /dev/null @@ -1,106 +0,0 @@ -electrum-tor-address: - name: Electrum Tor Address - description: The Tor address for the electrum interface. - type: pointer - subtype: package - package-id: electrs - target: tor-address - interface: electrum -bitcoind: - type: union - name: Bitcoin Core - description: | - The Bitcoin Core node to connect to: - - internal: The Bitcoin Core or Proxy services installed to your Embassy - - external: An unpruned Bitcoin Core node running on a different device - tag: - id: type - name: Type - variant-names: - internal: Internal (Bitcoin Core) - internal-proxy: Internal (Bitcoin Proxy) - external: External - description: | - The Bitcoin Core node to connect to: - - internal: The Bitcoin Core and Proxy services installed to your Embassy - - external: An unpruned Bitcoin Core node running on a different device - default: internal - variants: - internal: - user: - type: pointer - name: RPC Username - description: The username for Bitcoin Core's RPC interface - subtype: package - package-id: bitcoind - target: config - multi: false - selector: "$.rpc.username" - password: - type: pointer - name: RPC Password - description: The password for Bitcoin Core's RPC interface - subtype: package - package-id: bitcoind - target: config - multi: false - selector: "$.rpc.password" - internal-proxy: - user: - type: pointer - name: RPC Username - description: The username for the RPC user allocated to electrs - subtype: package - package-id: btc-rpc-proxy - target: config - multi: false - selector: '$.users[?(@.name == "electrs")].name' - password: - type: pointer - name: RPC Password - description: The password for the RPC user allocated to electrs - subtype: package - package-id: btc-rpc-proxy - target: config - multi: false - selector: '$.users[?(@.name == "electrs")].password' -advanced: - type: object - name: Advanced - description: Advanced settings for Bitcoin Proxy - nullable: false - spec: - log-filters: - type: enum - name: Log Filters - values: - - ERROR - - WARN - - INFO - - DEBUG - - TRACE - value-names: - ERROR: Error - WARN: Warning - INFO: Info - DEBUG: Debug - TRACE: Trace - default: "INFO" - index-batch-size: - type: number - name: Index Batch Size - description: | - Maximum number of blocks to request from bitcoind per batch - nullable: true - range: "[1,10000]" - integral: true - units: blocks - index-lookup-limit: - type: number - name: Index Lookup Limit - description: | - Number of transactions to lookup before returning an error, to prevent 'too popular' addresses from causing the RPC server to get stuck (0 - disable the limit)" - nullable: true - range: "[1,10000]" - integral: true - units: transactions diff --git a/assets/compat/dependencies.yaml b/assets/compat/dependencies.yaml deleted file mode 100644 index 02bfa4a..0000000 --- a/assets/compat/dependencies.yaml +++ /dev/null @@ -1,3 +0,0 @@ -bitcoind: - condition: '''bitcoind.type = "internal" OR ''bitcoind.type = "internal-proxy"' - health_checks: ["synced"] diff --git a/docs/instructions.md b/docs/instructions.md index 83dc9da..f9f7a2c 100644 --- a/docs/instructions.md +++ b/docs/instructions.md @@ -1,3 +1,21 @@ -# Instructions for Hello World +# Electrum Rust Server (electrs) -Instructions go here. \ No newline at end of file +`electrs` is an efficient re-implementation of Electrum Server written in Rust. Its main purpose is as an indexer for external wallets that use bitcoind as a backend. There are all sorts of wallets that connect to bitcoind using the Electrum protocol. This Embassy package allows you to run your own electrum server connected to your own Bitcoin Core node, which is the most private, uncensorable, yet fast and easy way to use Bitcoin. +In order to link a wallet, check out the guides at https://github.com/start9labs/electrs-wrapper/tree/master/docs/wallets.md. +# Syncing + +**WARNING: Make sure you have at least a gigabyte of free RAM before starting Electrs. If you don't, your system will grind to a halt and you will be very unhappy. Pay especially close attention if you're running Mastodon and you have a 4GB system. We recommend that you temporarily stop any services that use lots of RAM, especially Mastodon and Synapse. Keep in mind that Bitcoin Core will also use more RAM than usual during this initial sync.** + +When you first start Electrs, it will start building the indexes it needs in order to serve transactions to the wallets that subscribe to it. Electrs will not be usable until after this process completes. On an Embassy, this shouldn't take more than about a day. + +Once your electrum server is synced, it will start listening for subscriptions from external wallets. + +# Configuration + +Electrs on the Embassy requires a fully synced archival Bitcoin Core node as a source for blockchain data. It uses both the RPC interface and the peer interface of `bitcoind` in order to function. This requirement will be automatically enforced by EmbassyOS. + +**Bitcoin Core vs Bitcoin Proxy** + +If you choose Bitcoin Proxy as your blockchain source, when Electrum makes RPC requests, these will go through Bitcoin Proxy instead of directly to Bitcoin Core. This allows you to control access to your bitcoin node by Electrs and any wallets that are using it. + +Note that if you use Proxy as your RPC server, your internal Bitcoin Core node will still be needed as Electrs pulls data from the bitcoin p2p protocol as well, and Proxy does not support serving data via the bitcoin p2p protocol. diff --git a/docs/integrations/bitbox/assets/bitbox0.png b/docs/integrations/bitbox/assets/bitbox0.png new file mode 100644 index 0000000..9272435 Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox0.png differ diff --git a/docs/integrations/bitbox/assets/bitbox1.png b/docs/integrations/bitbox/assets/bitbox1.png new file mode 100644 index 0000000..24f7604 Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox1.png differ diff --git a/docs/integrations/bitbox/assets/bitbox2.png b/docs/integrations/bitbox/assets/bitbox2.png new file mode 100644 index 0000000..ff91147 Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox2.png differ diff --git a/docs/integrations/bitbox/assets/bitbox3.png b/docs/integrations/bitbox/assets/bitbox3.png new file mode 100644 index 0000000..88a07bd Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox3.png differ diff --git a/docs/integrations/bitbox/assets/bitbox4.png b/docs/integrations/bitbox/assets/bitbox4.png new file mode 100644 index 0000000..2d5f60c Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox4.png differ diff --git a/docs/integrations/bitbox/assets/bitbox5.png b/docs/integrations/bitbox/assets/bitbox5.png new file mode 100644 index 0000000..97424ac Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox5.png differ diff --git a/docs/integrations/bitbox/assets/bitbox6.png b/docs/integrations/bitbox/assets/bitbox6.png new file mode 100644 index 0000000..fe88571 Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox6.png differ diff --git a/docs/integrations/bitbox/assets/bitbox7.png b/docs/integrations/bitbox/assets/bitbox7.png new file mode 100644 index 0000000..023ce2f Binary files /dev/null and b/docs/integrations/bitbox/assets/bitbox7.png differ diff --git a/docs/integrations/bitbox/guide.md b/docs/integrations/bitbox/guide.md new file mode 100644 index 0000000..491f78f --- /dev/null +++ b/docs/integrations/bitbox/guide.md @@ -0,0 +1,36 @@ +# BitBox Integration Setup + +Note: You will need to be running Tor on your device, find guides to set this up here: https://start9.com/latest/user-manual/connecting/connecting-tor/tor-os/index + +1. Open the BitBox App and go to "Settings" + + ![BitBox](./assets/bitbox0.png) + +1. Click "Enable Tor Proxy" + + ![Tor](./assets/bitbox1.png) + +1. Enable the proxy and enter "127.0.0.1:9050" as the address, then click "Set Proxy Address" + + ![Tor](./assets/bitbox2.png) + +1. Back out and click on "Connect your own full node" + + ![Full Node](./assets/bitbox3.png) + + - Note: You may wish to remove the default servers + +1. Under "Add a server," add your electrs onion address from your electrs service page (found in your Embassy's electrs service page, under "Interfaces"), followed by :50001 for the port, as shown. Remove the "http://" prefix, or BitBox will +give an error. Click "Check" to test. + + ![Add Server](./assets/bitbox4.png) + +1. If you get the following error, simply restart the app (Tor has not connected) and repeat from Step 4. + + ![Error](./assets/bitbox5.png) + +1. You should see the following confirming a successful connection. Click "Add" to finalize the addition of your node to BitBox. You will then see it under the list of servers above. + + ![Success](./assets/bitbox6.png) + +1. Restart the app and you're ready to use BitBox!! diff --git a/docs/integrations/bluewallet/guide.md b/docs/integrations/bluewallet/guide.md new file mode 100644 index 0000000..fe0cfe7 --- /dev/null +++ b/docs/integrations/bluewallet/guide.md @@ -0,0 +1,13 @@ +# BlueWallet Integration Setup + +Note: iOS is NOT currently supported as the internal Tor daemon is unstable. You will need to be running Tor on your device, find guides to set this up here: https://start9.com/latest/user-manual/connecting/connecting-tor/tor-os/index + +1. Ensure Orbot is running (in VPN mode) and that you have added BlueWallet to the VPN list, as described in the link above. BlueWallet has a built in Tor daemon, but it has not been tested to work properly. + +1. Open BlueWallet and navigate to the top-right menu -> "Network" -> "Tor Settings," and tap the button at the top for "Disabled." + +1. Go back one page to Network and select "Electrum Server." + +1. Enter your electrs Tor address (found in your Embassy's electrs service page, under "Interfaces"), removing the "http://" prefix. Then add "50001" for the port and tap "Save." + +1. That's it! You should see a success message. \ No newline at end of file diff --git a/docs/integrations/electrum/assets/electrum0.png b/docs/integrations/electrum/assets/electrum0.png new file mode 100644 index 0000000..3b4fab8 Binary files /dev/null and b/docs/integrations/electrum/assets/electrum0.png differ diff --git a/docs/integrations/electrum/assets/electrum1.png b/docs/integrations/electrum/assets/electrum1.png new file mode 100644 index 0000000..0273673 Binary files /dev/null and b/docs/integrations/electrum/assets/electrum1.png differ diff --git a/docs/integrations/electrum/assets/electrum2.png b/docs/integrations/electrum/assets/electrum2.png new file mode 100644 index 0000000..4319b9e Binary files /dev/null and b/docs/integrations/electrum/assets/electrum2.png differ diff --git a/docs/integrations/electrum/assets/electrum3.png b/docs/integrations/electrum/assets/electrum3.png new file mode 100644 index 0000000..cfdcd62 Binary files /dev/null and b/docs/integrations/electrum/assets/electrum3.png differ diff --git a/docs/integrations/electrum/assets/electrum4.png b/docs/integrations/electrum/assets/electrum4.png new file mode 100644 index 0000000..84568ab Binary files /dev/null and b/docs/integrations/electrum/assets/electrum4.png differ diff --git a/docs/integrations/electrum/guide.md b/docs/integrations/electrum/guide.md new file mode 100644 index 0000000..03ff4a8 --- /dev/null +++ b/docs/integrations/electrum/guide.md @@ -0,0 +1,21 @@ +# Electrum Integration Setup + +Note: You will need to be running Tor on your device, find guides to set this up here: https://start9.com/latest/user-manual/connecting/connecting-tor/tor-os/index + +Caution: This will add your Electrum server, but if you want to ONLY connect to your server, you will need to run Electrum in [Single Server Mode](https://electrum.readthedocs.io/en/latest/tor.html) from the CLI. + +1. Open Electrum and go to "Tools -> Network," or if you are running for the first time, choose "Select server manually," and click "Next." + + ![Select Server](./assets/electrum0.png) + +1. On the following screen, uncheck "Select server automatically," and add in your electrs onion address (found in your Embassy's electrs service page, under "Interfaces"). Remove the "http://" prefix, and add ":50001:t" to indicate the port (50001) and protocol (TCP). Then click "Next." + + ![Server Setup](./assets/electrum2.png) + +1. Select "Use Tor" and "Use Proxy" and enter "127.0.0.1" for the address and 9050 for the port. Click "Next." + + ![Enable Tor Proxy](./assets/electrum3.png) + +1. That's it! You will be prompted to create a wallet if this is your first time. You can check your connection by clicking the orb in the bottom right, which should be blue in color. If your server settings persist, you are connected. + + ![Add Server](./assets/electrum4.png) diff --git a/docs/integrations/sparrow/assets/sparrow0.png b/docs/integrations/sparrow/assets/sparrow0.png new file mode 100644 index 0000000..9b4ac8d Binary files /dev/null and b/docs/integrations/sparrow/assets/sparrow0.png differ diff --git a/docs/integrations/sparrow/assets/sparrow1.png b/docs/integrations/sparrow/assets/sparrow1.png new file mode 100644 index 0000000..61ce1d4 Binary files /dev/null and b/docs/integrations/sparrow/assets/sparrow1.png differ diff --git a/docs/integrations/sparrow/assets/sparrow2.png b/docs/integrations/sparrow/assets/sparrow2.png new file mode 100644 index 0000000..da80231 Binary files /dev/null and b/docs/integrations/sparrow/assets/sparrow2.png differ diff --git a/docs/integrations/sparrow/assets/sparrow3.png b/docs/integrations/sparrow/assets/sparrow3.png new file mode 100644 index 0000000..68a00b3 Binary files /dev/null and b/docs/integrations/sparrow/assets/sparrow3.png differ diff --git a/docs/integrations/sparrow/assets/sparrow4.png b/docs/integrations/sparrow/assets/sparrow4.png new file mode 100644 index 0000000..2a0f037 Binary files /dev/null and b/docs/integrations/sparrow/assets/sparrow4.png differ diff --git a/docs/integrations/sparrow/guide.md b/docs/integrations/sparrow/guide.md new file mode 100644 index 0000000..009368a --- /dev/null +++ b/docs/integrations/sparrow/guide.md @@ -0,0 +1,23 @@ +# Sparrow Integration Setup + +Note: You will need to be running Tor on your device, find guides to set this up here: https://start9.com/latest/user-manual/connecting/connecting-tor/tor-os/index + +1. Open Sparrow and go to "File -> Preferences -> Server," or if you are running for the first time, proceed through the introduction until the screen below. Then select "Configure Server." + + ![Configure Server](./assets/sparrow0.png) + +1. On the following screen, select "Private Electrum Server." + + ![Electrum Server](./assets/sparrow1.png) + +1. Enter your electrs Tor address (found in your Embassy's electrs service page, under "Interfaces"), removing the "http://" prefix. Enable "Use Proxy" and then enter "127.0.0.1" for the address and "9050" for the port. + + ![Server Setup](./assets/sparrow2.png) + +1. Click "Test Connection." + + ![Test](./assets/sparrow3.png) + +1. You should see the success message below, then you may return to your wallet, or proceed to create a new one. + + ![Success](./assets/sparrow4.png) \ No newline at end of file diff --git a/docs/integrations/trezor/assets/trezor0.png b/docs/integrations/trezor/assets/trezor0.png new file mode 100644 index 0000000..a7410a7 Binary files /dev/null and b/docs/integrations/trezor/assets/trezor0.png differ diff --git a/docs/integrations/trezor/assets/trezor1.png b/docs/integrations/trezor/assets/trezor1.png new file mode 100644 index 0000000..6d3ae3d Binary files /dev/null and b/docs/integrations/trezor/assets/trezor1.png differ diff --git a/docs/integrations/trezor/assets/trezor2.png b/docs/integrations/trezor/assets/trezor2.png new file mode 100644 index 0000000..f834fe1 Binary files /dev/null and b/docs/integrations/trezor/assets/trezor2.png differ diff --git a/docs/integrations/trezor/assets/trezor3.png b/docs/integrations/trezor/assets/trezor3.png new file mode 100644 index 0000000..d124a08 Binary files /dev/null and b/docs/integrations/trezor/assets/trezor3.png differ diff --git a/docs/integrations/trezor/assets/trezor4.png b/docs/integrations/trezor/assets/trezor4.png new file mode 100644 index 0000000..d97be5d Binary files /dev/null and b/docs/integrations/trezor/assets/trezor4.png differ diff --git a/docs/integrations/trezor/assets/trezor5.png b/docs/integrations/trezor/assets/trezor5.png new file mode 100644 index 0000000..d33e6d0 Binary files /dev/null and b/docs/integrations/trezor/assets/trezor5.png differ diff --git a/docs/integrations/trezor/guide.md b/docs/integrations/trezor/guide.md new file mode 100644 index 0000000..a022e8b --- /dev/null +++ b/docs/integrations/trezor/guide.md @@ -0,0 +1,29 @@ +# Trezor Integration Setup + +Note: You will need to be running Tor on your device, find guides to set this up here: https://start9.com/latest/user-manual/connecting/connecting-tor/tor-os/index + +1. Open Trezor and click on the gear icon in the top left to enter the Settings. + + ![Trezor Settings](./assets/trezor0.png "Trezor Settings icon") + +1. On the following screen, select the "Crypto" submenu. + + ![Crypto Settings](./assets/trezor1.png "Trezor Crypto Settings Submenu") + +1. On this screen, hover over Bitcoin to show the gear icon, then click on that icon to enter Bitcoin's Settings menu. + + ![Bitcoin Settings](./assets/trezor2.png "Bitocin Settings icon") + +1. Under "Backends," click the dropdown menu and select "Custom Electrum Server." + + ![Trezor Backends](./assets/trezor3.png "Trezor Backend Selection") + +1. Enter your electrs Tor address (found in your Embassy's electrs service page, under "Interfaces"). Remove the "http://" prefix, then add `:50001:t` at the end, to signify port 50001, TCP connection. Click "Confirm." + + ![Electrs Address](./assets/trezor4.png "Enter electrs address, port, and tcp setting") + +1. If you have not already enabled Tor in the Trezor settings, you will be asked to do so now. Click "Enable Tor and Confirm," and that's it! You're now using your Embassy's Bitcoin node with your Trezor Suite! + + ![Enable Tor](./assets/trezor5.png "Enable Tor and Confirm") + + Note: If you are required to enter Tor address and port, use the default: `127.0.0.1:9050` \ No newline at end of file diff --git a/docs/wallets.md b/docs/wallets.md new file mode 100644 index 0000000..5284d50 --- /dev/null +++ b/docs/wallets.md @@ -0,0 +1,65 @@ +# Tested Wallets + +## BitBox App + +https://shiftcrypto.ch/download/ + +Available for: +- Android +- Linux +- Mac +- Windows + +[Setup guide](./integrations/bitbox/guide.md) + +## BlueWallet + +https://bluewallet.io/ + +Available for: +- Android +- iOS (Not currently supported due to bugs in the app) + +[Setup guide](./integrations/bluewallet/guide.md) + +## Coinomi + +**Not supported - requires Tor support by Coinomi team** + +## Electrum + +https://electrum.org/#download + +Available for: +- Android +- Linux +- Mac +- Windows + +[Setup guide](./integrations/electrum/guide.md) + +## Muun + +**Not supported - requires electrum support and Tor support by Muun team (https://github.com/muun/apollo/issues/83)** + +## Sparrow + +https://sparrowwallet.com/download/ + +Available for: +- Linux +- Mac +- Windows + +[Setup guide](./integrations/sparrow/guide.md) + +## Trezor Suite + +https://suite.trezor.io/ + +Available for: +- Linux +- Mac +- Windows + +[Setup guide](./integrations/trezor/guide.md) diff --git a/electrs b/electrs index 45f35c6..446858e 160000 --- a/electrs +++ b/electrs @@ -1 +1 @@ -Subproject commit 45f35c6755e18c047ed3668cccc6d625da4a56e7 +Subproject commit 446858ea621416916f84cbce61be92b748e8133e diff --git a/instructions.md b/instructions.md deleted file mode 100644 index c940b09..0000000 --- a/instructions.md +++ /dev/null @@ -1,22 +0,0 @@ - -# Electrum Rust Server (electrs) - -`electrs` is an efficient re-implementation of Electrum Server written in Rust. Its main purpose is as an indexer for external wallets that use bitcoind as a backend. There are all sorts of wallets that connect to bitcoind using the Electrum protocol. This Embassy package allows you to run your own electrum server connected to your own Bitcoin Core node, which is the most private, uncensorable, yet fast and easy way to use bitcoin. - -# Syncing - -**WARNING: Make sure you have at least a gigabyte of free RAM before starting Electrs. If you don't, your system will grind to a halt and you will be very unhappy. Pay especially close attention if you're running Mastodon and you have a 4GB system. We recommend that you temporarily stop any services that use lots of RAM, especially Mastodon and Synapse. Keep in mind that Bitcoin Core will also use more RAM than usual during this initial sync.** - -When you first start Electrs, it will start building the indexes it needs in order to serve transactions to the wallets that subscribe to it. Electrs will not be usable until after this process completes. On an Embassy, this shouldn't take more than about a day. - -Once your electrum server is synced, it will start listening for subscriptions from external wallets. - -# Configuration - -Electrs on the Embassy requires a fully synced archival Bitcoin Core node as a source for blockchain data. It uses both the RPC interface and the peer interface of bitcoind in order to function. If you choose an external bitcoin node as your source, make sure it's a full archival node. If you choose the internal Bitcoin Core or Bitcoin Proxy, this requirement will be automatically enforced. - -**Bitcoin Core vs Bitcoin Proxy** - -If you choose Internal (Bitcoin Proxy) as your blockchain source, when Electrum makes RPC requests, these will go through Bitcoin Proxy instead of directly to Bitcoin Core. This allows you to control access to your bitcoin node by Electrs and any wallets that are using it. - -Note that if you use Proxy as your RPC server, your internal Bitcoin Core node will still be needed as Electrs pulls data from the bitcoin p2p protocol as well, and Proxy does not support serving data via the bitcoin p2p protocol. diff --git a/instructions.md b/instructions.md new file mode 120000 index 0000000..0aa7f4e --- /dev/null +++ b/instructions.md @@ -0,0 +1 @@ +docs/instructions.md \ No newline at end of file diff --git a/manifest.yaml b/manifest.yaml index 4c5fc0b..57a4a97 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,9 +1,8 @@ -# v0.3.0 and up Manifest example written in yaml (toml and json are also acceptable) - id: electrs title: "electrs" -version: 0.9.7 -release-notes: "Initial release for EmbassyOS" +version: 0.9.9 +release-notes: | + * Update upstream to [v0.9.9](https://github.com/romanz/electrs/releases/tag/v0.9.9) license: mit wrapper-repo: "https://github.com/Start9Labs/electrs-wrapper" upstream-repo: "https://github.com/romanz/electrs" @@ -49,52 +48,14 @@ health-checks: io-format: json config: get: - type: docker - image: compat - system: true - entrypoint: compat - args: - - config - - get - - /root - - "/mnt/assets/config_spec.yaml" - mounts: - compat: /mnt/assets - main: /root - io-format: yaml + type: script set: - type: docker - image: compat - system: true - entrypoint: compat - args: - - config - - set - - electrs - - /root - - "/mnt/assets/config_rules.yaml" - - "/mnt/assets/dependencies.yaml" - mounts: - compat: /mnt/assets - main: /root - io-format: yaml + type: script properties: - type: docker - image: compat - system: true - entrypoint: compat - args: - - properties - - /root - mounts: - main: /root - compat: /mnt/assets - io-format: yaml + type: script volumes: main: type: data - compat: - type: assets interfaces: electrum: name: Electrum Interface @@ -119,80 +80,24 @@ dependencies: version: ">=0.3.2.1 <0.4.0" requirement: type: "opt-out" - how: Set "Bitcoin Core" to "Internal (Bitcoin Core)" or "External" + how: Set "Bitcoin Core" to 'Bitcoin Core'" description: Provides bitcoin rpc interface needed for info about fees, blockchain, mempool, network, and transactions. Can also use bitcoind for rpc interface, or an external node for both. config: check: - type: docker - image: compat - system: true - entrypoint: compat - args: - - dependency - - check - - electrs - - "btc-rpc-proxy" - - /data - - "/mnt/assets/btc-rpc-proxy.rules.yaml" - mounts: - compat: /mnt/assets - main: /data - io-format: yaml + type: script auto-configure: - type: docker - image: compat - system: true - entrypoint: compat - args: - - dependency - - "auto-configure" - - electrs - - "btc-rpc-proxy" - - /data - - "/mnt/assets/btc-rpc-proxy.rules.yaml" - mounts: - compat: /mnt/assets - main: /data - io-format: yaml + type: script bitcoind: - version: ">=0.21.1.2 <23.0.0" + version: ">=0.21.1.2 <24.0.0" requirement: type: "opt-out" how: Set "Bitcoin Core" type to "External" description: Needed for peer interface and rpc interface. Can also use btc-rpc-proxy for rpc interface, or an external node for both. config: check: - type: docker - image: compat - system: true - entrypoint: compat - args: - - dependency - - check - - electrs - - bitcoind - - /data - - "/mnt/assets/bitcoind.rules.yaml" - mounts: - compat: /mnt/assets - main: /data - io-format: yaml + type: script auto-configure: - type: docker - image: compat - system: true - entrypoint: compat - args: - - dependency - - "auto-configure" - - electrs - - bitcoind - - /data - - "/mnt/assets/bitcoind.rules.yaml" - mounts: - compat: /mnt/assets - main: /data - io-format: yaml + type: script backup: create: type: docker @@ -222,18 +127,10 @@ backup: main: /data migrations: from: - "<=0.9.7": - type: docker - image: main - entrypoint: sh - args: ["-c", 'json=''{"configured": true }'' && echo "$json"'] - io-format: json - inject: true + "*": + type: script + args: ["from"] to: - "<=0.9.7": - type: docker - image: main - entrypoint: sh - args: ["-c", 'json=''{"configured": true }'' && echo "$json"'] - io-format: json - inject: true + "*": + type: script + args: ["to"] diff --git a/scripts/deps.ts b/scripts/deps.ts new file mode 100644 index 0000000..628bc25 --- /dev/null +++ b/scripts/deps.ts @@ -0,0 +1 @@ +export * from "https://deno.land/x/embassyd_sdk@v0.3.1.1.2/mod.ts"; diff --git a/scripts/embassy.ts b/scripts/embassy.ts new file mode 100644 index 0000000..73cfa84 --- /dev/null +++ b/scripts/embassy.ts @@ -0,0 +1,5 @@ +export { setConfig } from "./services/setConfig.ts"; +export { dependencies } from "./services/dependencies.ts"; +export { properties } from "./services/properties.ts"; +export { getConfig } from "./services/getConfig.ts"; +export { migration } from "./services/migrations.ts"; diff --git a/scripts/services/dependencies.ts b/scripts/services/dependencies.ts new file mode 100644 index 0000000..8188450 --- /dev/null +++ b/scripts/services/dependencies.ts @@ -0,0 +1,214 @@ +import { types as T, matches } from "../deps.ts"; + +const { shape, arrayOf, string, boolean } = matches; + +const matchProxyConfig = shape({ + users: arrayOf( + shape( + { + name: string, + "allowed-calls": arrayOf(string), + password: string, + "fetch-blocks": boolean, + }, + ["fetch-blocks"] + ) + ), +}); + +function times(fn: (i: number) => T, amount: number): T[] { + const answer = new Array(amount); + for (let i = 0; i < amount; i++) { + answer[i] = fn(i); + } + return answer; +} + +function randomItemString(input: string) { + return input[Math.floor(Math.random() * input.length)]; +} + +const serviceName = "electrs"; +const fullChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +type Check = { + currentError(config: T.Config): string | void; + fix(config: T.Config): void; +}; + +const proxyChecks: Array = [ + { + currentError(config) { + if (!matchProxyConfig.test(config)) { + return "Config is not the correct shape"; + } + if (config.users.some((x) => x.name === serviceName)) { + return; + } + return `Must have an RPC user named "${serviceName}"`; + }, + fix(config) { + if (!matchProxyConfig.test(config)) { + return + } + config.users.push({ + name: serviceName, + "allowed-calls": [], + password: times(() => randomItemString(fullChars), 22).join(""), + }); + }, + }, + ...[ + "estimatesmartfee", + "getblockchaininfo", + "getblockcount", + "getmempoolentry", + "getnetworkinfo", + "getrawmempool", + "getrawtransaction", + + ].map( + (operator): Check => ({ + currentError(config) { + if (!matchProxyConfig.test(config)) { + return "Config is not the correct shape"; + } + if (config.users.find((x) => x.name === serviceName)?.["allowed-calls"]?.some((x) => x === operator) ?? false) { + return; + } + return `RPC user "${serviceName}" must have "${operator}" enabled`; + }, + fix(config) { + if (!matchProxyConfig.test(config)) { + throw new Error("Config is not the correct shape"); + } + const found = config.users.find((x) => x.name === serviceName); + if (!found) { + throw new Error(`No user ${serviceName} found`); + } + found["allowed-calls"] = [...(found["allowed-calls"] ?? []), operator]; + }, + }) + ), +]; + +const matchBitcoindConfig = shape({ + rpc: shape({ + enable: boolean, + }), + advanced: shape({ + peers: shape({ + listen: boolean, + }), + pruning: shape({ + mode: string, + }), + }), +}); + + +const bitcoindChecks: Array = [ + { + currentError(config) { + if (!matchBitcoindConfig.test(config)) { + return "Config is not the correct shape"; + } + if (!config.rpc.enable) { + return "Must have RPC enabled"; + } + return; + }, + fix(config) { + if (!matchBitcoindConfig.test(config)) { + return; + } + config.rpc.enable = true; + }, + }, + { + currentError(config) { + if (!matchBitcoindConfig.test(config)) { + return "Config is not the correct shape"; + } + if (!config.advanced.peers.listen) { + return "Must have peer interface enabled"; + } + return; + }, + fix(config) { + if (!matchBitcoindConfig.test(config)) { + return; + } + config.advanced.peers.listen = true; + }, + }, + { + currentError(config) { + if (!matchBitcoindConfig.test(config)) { + return "Config is not the correct shape"; + } + if (config.advanced.pruning.mode !== "disabled") { + return "Pruning must be disabled (must be an archival node)"; + } + return; + }, + fix(config) { + if (!matchBitcoindConfig.test(config)) { + return; + } + config.advanced.pruning.mode = "disabled"; + }, + }, +]; + +export const dependencies: T.ExpectedExports.dependencies = { + "btc-rpc-proxy": { + // deno-lint-ignore require-await + async check(effects, configInput) { + effects.info("check btc-rpc-proxy"); + for (const checker of proxyChecks) { + const error = checker.currentError(configInput); + if (error) { + effects.error(`throwing error: ${error}`); + return { error }; + } + } + return { result: null }; + }, + // deno-lint-ignore require-await + async autoConfigure(effects, configInput) { + effects.info("autoconfigure btc-rpc-proxy"); + for (const checker of proxyChecks) { + const error = checker.currentError(configInput); + if (error) { + checker.fix(configInput); + } + } + return { result: configInput }; + }, + }, + bitcoind: { + // deno-lint-ignore require-await + async check(effects, configInput) { + effects.info("check bitcoind"); + for (const checker of bitcoindChecks) { + const error = checker.currentError(configInput); + if (error) { + effects.error(`throwing error: ${error}`); + return { error }; + } + } + return { result: null }; + }, + // deno-lint-ignore require-await + async autoConfigure(effects, configInput) { + effects.info("autoconfigure bitcoind"); + for (const checker of bitcoindChecks) { + const error = checker.currentError(configInput); + if (error) { + checker.fix(configInput); + } + } + return { result: configInput }; + }, + }, +}; diff --git a/scripts/services/getConfig.ts b/scripts/services/getConfig.ts new file mode 100644 index 0000000..0a79d1e --- /dev/null +++ b/scripts/services/getConfig.ts @@ -0,0 +1,119 @@ +import { types as T, compat } from "../deps.ts"; + + +export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ + "electrum-tor-address": { + "name": "Electrum Tor Address", + "description": "The Tor address for the electrum interface.", + "type": "pointer", + "subtype": "package", + "package-id": "electrs", + "target": "tor-address", + "interface": "electrum" + }, + "bitcoind": { + "type": "union", + "name": "Bitcoin Core", + "description": "The Bitcoin Core node to connect to", + "tag": { + "id": "type", + "name": "Type", + "variant-names": { + "internal": "Bitcoin Core", + "internal-proxy": "Bitcoin Proxy", + }, + "description": "Options
  • Bitcoin Core: the Bitcoin Core node installed on your Embassy
  • Bitcoin Proxy: the Bitcoin Proxy service installed on your Embassy
", + }, + "default": "internal-proxy", + "variants": { + "internal": { + "user": { + "type": "pointer", + "name": "RPC Username", + "description": "The username for Bitcoin Core's RPC interface", + "subtype": "package", + "package-id": "bitcoind", + "target": "config", + "multi": false, + "selector": "$.rpc.username" + }, + "password": { + "type": "pointer", + "name": "RPC Password", + "description": "The password for Bitcoin Core's RPC interface", + "subtype": "package", + "package-id": "bitcoind", + "target": "config", + "multi": false, + "selector": "$.rpc.password" + } + }, + "internal-proxy": { + "user": { + "type": "pointer", + "name": "RPC Username", + "description": "The username for the RPC user allocated to electrs", + "subtype": "package", + "package-id": "btc-rpc-proxy", + "target": "config", + "multi": false, + "selector": "$.users[?(@.name == \"electrs\")].name" + }, + "password": { + "type": "pointer", + "name": "RPC Password", + "description": "The password for the RPC user allocated to electrs", + "subtype": "package", + "package-id": "btc-rpc-proxy", + "target": "config", + "multi": false, + "selector": "$.users[?(@.name == \"electrs\")].password" + } + } + } + }, + "advanced": { + "type": "object", + "name": "Advanced", + "description": "Advanced settings for Bitcoin Proxy", + "spec": { + "log-filters": { + "type": "enum", + "name": "Log Filters", + "values": [ + "ERROR", + "WARN", + "INFO", + "DEBUG", + "TRACE" + ], + "value-names": { + "ERROR": "Error", + "WARN": "Warning", + "INFO": "Info", + "DEBUG": "Debug", + "TRACE": "Trace" + }, + "default": "INFO" + }, + "index-batch-size": { + "type": "number", + "name": "Index Batch Size", + "description": "Maximum number of blocks to request from bitcoind per batch\n", + "nullable": true, + "range": "[1,10000]", + "integral": true, + "units": "blocks" + }, + "index-lookup-limit": { + "type": "number", + "name": "Index Lookup Limit", + "description": "Number of transactions to lookup before returning an error, to prevent 'too popular' addresses from causing the RPC server to get stuck (0 - disable the limit)\"\n", + "nullable": true, + "range": "[1,10000]", + "integral": true, + "units": "transactions" + } + } + } +}) diff --git a/scripts/services/migrations.ts b/scripts/services/migrations.ts new file mode 100644 index 0000000..384cd00 --- /dev/null +++ b/scripts/services/migrations.ts @@ -0,0 +1,4 @@ +import { compat, types as T } from "../deps.ts"; + +export const migration: T.ExpectedExports.migration = compat.migrations + .fromMapping( {}, "0.9.9" ); diff --git a/scripts/services/properties.ts b/scripts/services/properties.ts new file mode 100644 index 0000000..1497297 --- /dev/null +++ b/scripts/services/properties.ts @@ -0,0 +1,3 @@ +import { compat, types as T, YAML } from "../deps.ts"; + +export const properties: T.ExpectedExports.properties = compat.properties diff --git a/scripts/services/setConfig.ts b/scripts/services/setConfig.ts new file mode 100644 index 0000000..7ed87f4 --- /dev/null +++ b/scripts/services/setConfig.ts @@ -0,0 +1,13 @@ +import { + compat, + types as T, +} from "../deps.ts"; + +// deno-lint-ignore no-explicit-any +export const setConfig: T.ExpectedExports.setConfig = async (effects, input: any) => { + + const depBitcoin: T.DependsOn = input?.bitcoind?.type === "internal" || input?.bitcoind?.type === "internal-proxy" ? { bitcoind: ["synced"] } : {} + return await compat.setConfig(effects, input, { + ...depBitcoin + }); +}