diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d5a075 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +jam.s9pk +image.tar +scripts/*.js \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..57b3c95 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "jam-docker"] + path = jam-docker + url = https://github.com/joinmarket-webui/jam-docker.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ffd7efd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM ghcr.io/joinmarket-webui/joinmarket-webui-standalone:v0.0.10-clientserver-v0.9.6@sha256:12bba3c1761a277a7e642cd0e9c29c97cc73fb697dc8f652a4e57bd2f99d89f3 +RUN apt-get update && apt-get install -qq --no-install-recommends wget bash +RUN wget https://github.com/mikefarah/yq/releases/download/v4.12.2/yq_linux_arm.tar.gz -O - |\ + tar xz && mv yq_linux_arm /usr/bin/yq + +USER root + +ENV DATADIR /root/.joinmarket +ENV CONFIG ${DATADIR}/joinmarket.cfg +ENV DEFAULT_CONFIG /default.cfg +ENV DEFAULT_AUTO_START /autostart +ENV AUTO_START ${DATADIR}/autostart +ENV JM_RPC_HOST="bitcoind.embassy" +ENV JM_RPC_PORT="8332" +ENV JM_RPC_USER="bitcoin" +ENV JM_RPC_PASSWORD= +ENV APP_USER "joinmarket" +ENV APP_PASSWORD "joinmarket" +ENV JMWEBUI_JMWALLETD_HOST "jam.embassy" +ENV JMWEBUI_JMWALLETD_API_PORT "28183" +ENV JMWEBUI_JMWALLETD_WEBSOCKET_PORT "28283" +ENV ENSURE_WALLET true + +ADD jam-docker/standalone/autostart / +ADD jam-docker/standalone/default.cfg / +ADD jam-docker/standalone/torrc / +RUN chmod a+x /autostart +RUN chmod a+x /default.cfg +RUN chmod a+x /torrc + +ADD docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh +ADD assets/utils/check-web.sh /usr/local/bin/check-web.sh +ADD assets/utils/check-api.sh /usr/local/bin/check-api.sh +ADD actions/unlock-wallet.sh /usr/local/bin/unlock-wallet.sh +RUN chmod a+x /usr/local/bin/*.sh + +EXPOSE 80 28183 27183 8080 + +ENTRYPOINT ["/usr/local/bin/docker_entrypoint.sh"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..70063c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Start9 Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..82e791f --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +ASSETS := $(shell yq e '.assets.[].src' manifest.yaml) +ASSET_PATHS := $(addprefix assets/,$(ASSETS)) +VERSION := $(shell yq e ".version" manifest.yaml) +S9PK_PATH=$(shell find . -name jam.s9pk -print) + +# delete the target of a rule if it has changed and its recipe exits with a nonzero exit status +.DELETE_ON_ERROR: + +all: verify + +verify: jam.s9pk $(S9PK_PATH) + embassy-sdk verify s9pk $(S9PK_PATH) + +install: all jam.s9pk + embassy-cli package install jam.s9pk + +clean: + rm -f jam.s9pk + rm -f image.tar + rm -f scripts/*.js + +jam.s9pk: manifest.yaml assets/compat/* image.tar scripts/embassy.js docs/instructions.md $(ASSET_PATHS) + embassy-sdk pack + +image.tar: Dockerfile docker_entrypoint.sh assets/utils/* + DOCKER_CLI_EXPERIMENTAL=enabled docker buildx build --tag start9/jam/main:$(VERSION) --platform=linux/arm64 -o type=docker,dest=image.tar . + +scripts/embassy.js: scripts/**/*.ts + deno bundle scripts/embassy.ts scripts/embassy.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1c8d08 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Wrapper for JAM + +JAM is a web UI for JoinMarket with focus on user-friendliness. It aims to provide sensible defaults and be easy to use for beginners while still providing the features advanced users expect. + +## Dependencies + +- [docker](https://docs.docker.com/get-docker) +- [docker-buildx](https://docs.docker.com/buildx/working-with-buildx/) +- [yq](https://mikefarah.gitbook.io/yq) +- [deno](https://deno.land/)s +- [embassy-sdk](https://github.com/Start9Labs/embassy-os/tree/master/backend) +- [make](https://www.gnu.org/software/make/) + +## Build enviroment +Prepare your EmbassyOS build enviroment. In this example we are using Ubuntu 20.04. + +1. Install docker +``` +curl -fsSL https://get.docker.com -o- | bash +sudo usermod -aG docker "$USER" +exec sudo su -l $USER +``` +2. Set buildx as the default builder +``` +docker buildx install +docker buildx create --use +``` +3. Enable cross-arch emulated builds in docker +``` +docker run --privileged --rm linuxkit/binfmt:v0.8 +``` +4. Install yq +``` +sudo snap install yq +``` +5. Install essentials build packages +``` +sudo apt-get install -y build-essential openssl libssl-dev libc6-dev clang libclang-dev ca-certificates +``` +6. Install Rust +``` +curl https://sh.rustup.rs -sSf | sh +# Choose nr 1 (default install) +source $HOME/.cargo/env +``` +7. Install toml +``` +cargo install toml-cli +``` +8. Build and install embassy-sdk +``` +cd ~/ && git clone https://github.com/Start9Labs/embassy-os.git +cd embassy-os/backend/ +./install-sdk.sh +``` + +## Cloning + +Clone the project locally. Note the submodule link to the original project(s). + +``` +git clone https://github.com/Start9Labs/jam-wrapper.git +cd jam-wrapper +git submodule update --init --recursive +``` +## Building + +To build the project, run the following commands: + +``` +docker run --rm --privileged multiarch/qemu-user-static --reset -p yes +docker buildx create --name multiarch --driver docker-container --use +docker buildx inspect --bootstrap +``` + +You should only run the above commands once to create a custom builder. Afterwards you will only need the below command to make the .s9pk file + +``` +make +``` + +## Installing (on Embassy) + +SSH into an Embassy device. +`scp` the `.s9pk` to any directory from your local machine. +Run the following command to install the package: + +``` +embassy-cli auth login +#Enter your embassy password then run: +embassy-cli package install /path/to/jam.s9pk +``` +## Verify Install + +Go to your Embassy Services page, select JAM and start the service. + +#Done diff --git a/actions/unlock-wallet.sh b/actions/unlock-wallet.sh new file mode 100755 index 0000000..2b5e834 --- /dev/null +++ b/actions/unlock-wallet.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -e + +count=`find /root/.joinmarket/wallets -type f -iname ".*.jmdat.lock" | wc -l` +if [ $count != 0 ] +then + rm -rf /root/.joinmarket/wallets/.*.jmdat.lock + export action_result_running=" { + \"version\": \"0\", + \"message\": \"All wallets unlocked.\", + \"value\": null, + \"copyable\": false, + \"qr\": false + }" >/dev/null && echo $action_result_running +else + export action_result_error=" { + \"version\": \"0\", + \"message\": \"No locked wallets found.\", + \"value\": null, + \"copyable\": false, + \"qr\": false + }" >/dev/null && echo $action_result_error +fi \ No newline at end of file diff --git a/assets/compat/bitcoind_config_rules.yaml b/assets/compat/bitcoind_config_rules.yaml new file mode 100644 index 0000000..b62420d --- /dev/null +++ b/assets/compat/bitcoind_config_rules.yaml @@ -0,0 +1,6 @@ +- rule: "rpc.enable?" + description: "Must have an RPC enabled" + suggestions: + - SET: + var: "rpc.enable" + to-value: true \ No newline at end of file diff --git a/assets/compat/btc-rpc-proxy_config_rules.yaml b/assets/compat/btc-rpc-proxy_config_rules.yaml new file mode 100644 index 0000000..3d76bbb --- /dev/null +++ b/assets/compat/btc-rpc-proxy_config_rules.yaml @@ -0,0 +1,85 @@ +- rule: '''users.*.name = "jam"' + description: 'Must have an RPC user named "jam"' + suggestions: + - PUSH: + to: "users" + value: + name: jam + allowed-calls: [] + - SET: + var: 'users.[first(item => ''item.name = "jam")].password' + to-entropy: + charset: "a-z,A-Z,0-9" + len: 22 +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getindexinfo"' + description: 'RPC user "jam" must have "getindexinfo" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getindexinfo" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getblockcount"' + description: 'RPC user "jam" must have "getblockcount" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getblockcount" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getchaintips"' + description: 'RPC user "jam" must have "getchaintips" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getchaintips" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getmempoolinfo"' + description: 'RPC user "jam" must have "getmempoolinfo" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getmempoolinfo" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getblockchaininfo"' + description: 'RPC user "jam" must have "getblockchaininfo" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getblockchaininfo" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getblockhash"' + description: 'RPC user "jam" must have "getblockhash" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getblockhash" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getblock"' + description: 'RPC user "jam" must have "getblock" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getblock" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getmempoolentry"' + description: 'RPC user "jam" must have "getmempoolentry" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getmempoolentry" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getrawtransaction"' + description: 'RPC user "jam" must have "getrawtransaction" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getrawtransaction" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "getrawmempool"' + description: 'RPC user "jam" must have "getrawmempool" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "getrawmempool" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "gettxout"' + description: 'RPC user "jam" must have "gettxout" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "gettxout" +- rule: '''users.[first(item => ''item.name = "jam")].allowed-calls.* = "validateaddress"' + description: 'RPC user "jam" must have "validateaddress" enabled' + suggestions: + - PUSH: + to: 'users.[first(item => ''item.name = "jam")].allowed-calls' + value: "validateaddress" \ No newline at end of file diff --git a/assets/compat/config_rules.yaml b/assets/compat/config_rules.yaml new file mode 100644 index 0000000..117c4f1 --- /dev/null +++ b/assets/compat/config_rules.yaml @@ -0,0 +1,3 @@ +# This is where any configuration rules related to the configuration would go. These ensure that the user can only create a valid config. + +[] diff --git a/assets/compat/config_spec.yaml b/assets/compat/config_spec.yaml new file mode 100644 index 0000000..81c783e --- /dev/null +++ b/assets/compat/config_spec.yaml @@ -0,0 +1,77 @@ +# To utilize the default config system built in the compat system image, this file is required. It defines the *structure* of the configuration file. These structured options display as changeable UI elements within the "Config" section of the service details page in the Embassy UI + +tor-address: + name: Network Tor Address + description: The Tor address for the network interface. + type: pointer + subtype: package + package-id: jam + target: tor-address + interface: main +lan-address: + name: Network LAN Address + description: The LAN address for the network interface. + type: pointer + subtype: package + package-id: jam + target: lan-address + interface: main +jm-wallet: + type: string + name: JAM Bitcoin Core Wallet + description: Bitcoin Core Wallet created for JAM + default: "jam_wallet" + nullable: false +bitcoind: + type: union + name: Bitcoin Core + description: The Bitcoin Core node for JoinMarket to connect to + tag: + id: type + name: Type + description: | + - Internal: The Bitcoin Core service installed to your Embassy + - Internal Proxy: The Bitcoin Proxy service installed on your Embassy + variant-names: + internal: Internal + internal-proxy: Internal (Bitcoin Proxy) + default: internal + variants: + internal: + user: + type: pointer + name: RPC Username + description: The username for the RPC user for Bitcoin Core + subtype: package + package-id: bitcoind + target: config + selector: "$.rpc.username" + multi: false + password: + type: pointer + name: RPC Password + description: The password for the RPC user for Bitcoin Core + subtype: package + package-id: bitcoind + target: config + selector: "$.rpc.password" + multi: false + internal-proxy: + user: + type: pointer + name: RPC Username + description: The username for the RPC user allocated to JoinMarket + subtype: package + package-id: btc-rpc-proxy + target: config + multi: false + selector: '$.users.[?(@.name == "jam")].name' + password: + type: pointer + name: RPC Password + description: The password for the RPC user allocated to JoinMarket + subtype: package + package-id: btc-rpc-proxy + target: config + multi: false + selector: '$.users.[?(@.name == "jam")].password' \ No newline at end of file diff --git a/assets/utils/check-api.sh b/assets/utils/check-api.sh new file mode 100644 index 0000000..99e3cf2 --- /dev/null +++ b/assets/utils/check-api.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +DURATION=$(&2 + exit 61 + fi + if ! curl -sSL --silent --fail --insecure https://jam.embassy:28183/api/v1/session &>/dev/null; then + echo "JAM API is unreachable" >&2 + exit 1 + fi +fi diff --git a/assets/utils/check-web.sh b/assets/utils/check-web.sh new file mode 100644 index 0000000..3db7c9a --- /dev/null +++ b/assets/utils/check-web.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +DURATION=$(/dev/null; then + echo "JAM Web UI is unreachable" >&2 + exit 1 + fi +fi diff --git a/autostart b/autostart new file mode 100644 index 0000000..44152be --- /dev/null +++ b/autostart @@ -0,0 +1,4 @@ +tor +nginx +ob-watcher +jmwalletd diff --git a/default.cfg b/default.cfg new file mode 100644 index 0000000..7826820 --- /dev/null +++ b/default.cfg @@ -0,0 +1,433 @@ + +[DAEMON] +# set to 1 to run the daemon service within this process; +# set to 0 if the daemon is run separately (using script joinmarketd.py) +no_daemon = 1 + +# Port on which daemon serves; note that communication still +# occurs over this port even if no_daemon = 1 +daemon_port = 27183 + +# Currently, running the daemon on a remote host is +# *NOT* supported, so don't change this variable +daemon_host = localhost + +# by default the client-daemon connection is plaintext, set to 'true' to use TLS; +# for this, you need to have a valid (self-signed) certificate installed +use_ssl = false + +[BLOCKCHAIN] +# options: bitcoin-rpc, regtest, bitcoin-rpc-no-history, no-blockchain +# When using bitcoin-rpc-no-history remember to increase the gap limit to scan for more addresses, try -g 5000 +# Use 'no-blockchain' to run the ob-watcher.py script in scripts/obwatch without current access +# to Bitcoin Core; note that use of this option for any other purpose is currently unsupported. +blockchain_source = bitcoin-rpc + +# options: signet, testnet, mainnet +# Note: for regtest, use network = testnet +network = mainnet + +rpc_host = localhost +# default ports are 8332 for mainnet, 18443 for regtest, 18332 for testnet, 38332 for signet +rpc_port = + +# Use either rpc_user / rpc_password pair or rpc_cookie_file. +rpc_user = bitcoin +rpc_password = password +#rpc_cookie_file = + +# rpc_wallet_file is Bitcoin Core wallet which is used for address and +# transaction monitoring (it is watchonly, no private keys are stored there). +# It must be created manually if does not exist, see docs/USAGE.md for more +# information. +rpc_wallet_file = + +[MESSAGING:onion] +# onion based message channels must have the exact type 'onion' +# (while the section name above can be MESSAGING:whatever), and there must +# be only ONE such message channel configured (note the directory servers +# can be multiple, below): +type = onion + +socks5_host = localhost +socks5_port = 9050 + +# the tor control configuration. +# for most people running the tor daemon +# on Linux, no changes are required here: +tor_control_host = localhost +# or, to use a UNIX socket +# tor_control_host = unix:/var/run/tor/control +# note: port needs to be provided (but is ignored for UNIX socket) +tor_control_port = 9051 + +# the host/port actually serving the hidden service +# (note the *virtual port*, that the client uses, +# is hardcoded to as per below 'directory node configuration'. +onion_serving_host = 127.0.0.1 +onion_serving_port = 8080 + +# directory node configuration +# +# This is mandatory for directory nodes (who must also set their +# own *.onion:port as the only directory in directory_nodes, below), +# but NOT TO BE USED by non-directory nodes (which is you, unless +# you know otherwise!), as it will greatly degrade your privacy. +# (note the default is no value, don't replace it with ""). +hidden_service_dir = +# +# This is a comma separated list (comma can be omitted if only one item). +# Each item has format host:port ; both are required, though port will +# be 5222 if created in this code. +# for MAINNET: +directory_nodes = 3kxw6lf5vf6y26emzwgibzhrzhmhqiw6ekrek3nqfjjmhwznb2moonad.onion:5222,jmdirjmioywe2s5jad7ts6kgcqg66rj6wujj6q77n6wbdrgocqwexzid.onion:5222,bqlpq6ak24mwvuixixitift4yu42nxchlilrcqwk2ugn45tdclg42qid.onion:5222 + +# for SIGNET (testing network): +# directory_nodes = rr6f6qtleiiwic45bby4zwmiwjrj3jsbmcvutwpqxjziaydjydkk5iad.onion:5222,k74oyetjqgcamsyhlym2vgbjtvhcrbxr4iowd4nv4zk5sehw4v665jad.onion:5222,y2ruswmdbsfl4hhwwiqz4m3sx6si5fr6l3pf62d4pms2b53wmagq3eqd.onion:5222 + +# This setting is ONLY for developer regtest setups, +# running multiple bots at once. Don't alter it otherwise +regtest_count = 0,0 + +## IRC SERVER 1: Darkscience IRC (Tor, IP) +################################################################################ +[MESSAGING:server1] +# by default the legacy format without a `type` field is +# understood to be IRC, but you can, optionally, add it: +# type = irc +channel = joinmarket-pit +port = 6697 +usessl = true + +# For traditional IP: +#host = irc.darkscience.net +#socks5 = false + +# For Tor (recommended as clearnet alternative): +host = darkirc6tqgpnwd3blln3yfv5ckl47eg7llfxkmtovrv7c7iwohhb6ad.onion +socks5 = true +socks5_host = localhost +socks5_port = 9050 + +## IRC SERVER 2: ILITA IRC (optional IRC alternate, Tor only) +################################################################################ +[MESSAGING:server2] +channel = joinmarket-pit +port = 6667 +usessl = false +socks5 = true +socks5_host = localhost + +host = ilitafrzzgxymv6umx2ux7kbz3imyeko6cnqkvy4nisjjj4qpqkrptid.onion +socks5_port = 9050 + +## IRC SERVER 3: (backup) hackint IRC (Tor, IP) +################################################################################ +#[MESSAGING:server3] +# channel = joinmarket-pit +# For traditional IP: +## host = irc.hackint.org +## port = 6697 +## usessl = true +## socks5 = false +# For Tor (default): +#host = ncwkrwxpq2ikcngxq3dy2xctuheniggtqeibvgofixpzvrwpa77tozqd.onion +#port = 6667 +#usessl = false +#socks5 = true +#socks5_host = localhost +#socks5_port = 9050 + +[LOGGING] +# Set the log level for the output to the terminal/console +# Possible choices: DEBUG / INFO / WARNING / ERROR +# Log level for the files in the logs-folder will always be DEBUG +console_log_level = INFO + +# Use color-coded log messages to help distinguish log levels?: +color = true + +[TIMEOUT] +maker_timeout_sec = 60 +unconfirm_timeout_sec = 180 +confirm_timeout_hours = 6 + +[POLICY] +# Use segwit style wallets and transactions +# Only set to false for old wallets, Joinmarket is now segwit only. +segwit = true + +# Use native segwit (bech32) wallet. If set to false, p2sh-p2wkh +# will be used when generating the addresses for this wallet. +# Notes: 1. The default joinmarket pit is native segwit. +# 2. You cannot change the type of a pre-existing wallet. +native = true + +# for dust sweeping, try merge_algorithm = gradual +# for more rapid dust sweeping, try merge_algorithm = greedy +# for most rapid dust sweeping, try merge_algorithm = greediest +# but don't forget to bump your miner fees! +merge_algorithm = default + +# The fee estimate is based on a projection of how many sats/kilo-vbyte +# are needed to get in one of the next N blocks. N is set here as +# the value of 'tx_fees'. This cost estimate is high if you set +# N=1, so we choose 3 for a more reasonable figure, as our default. +# You can also set your own fee/kilo-vbyte: any number higher than 1 thousand +# will be interpreted as the fee in sats/kilo-vbyte that you wish to use. +# +# Example: N=30000 will use 30 thousand sats/kilo-vbyte (30 sats/vB) as a fee, +# while N=5 will use the 5 block estimate from your selected blockchain source. +tx_fees = 3 + +# Transaction fee rate variance factor, 0.2 means 20% variation around +# any manually chosen values, so if you set tx_fees=10000 and +# tx_fees_factor=0.2, it might use any value between 8 thousand and 12 thousand +# for your transactions. +tx_fees_factor = 0.2 + +# For users getting transaction fee estimates over an API, +# place a sanity check limit on the sats/kilo-vbyte to be paid. +# This limit is also applied to users using Core, even though +# Core has its own sanity check limit, which is currently +# 1 million satoshis. +# +# Example: N=350000 will use 350 thousand sats/kilo-vbyte (350 sats/vB) as a +# maximum fee. +absurd_fee_per_kb = 350000 + +# In decimal, the maximum allowable change either lower or +# higher, that the fee rate used for coinjoin sweeps is +# allowed to be. +# (note: coinjoin sweeps *must estimate* fee rates; +# they cannot be exact due to the lack of change output.) +# +# Example: max_sweep_fee_change = 0.4, with tx_fees = 10000, +# means actual fee rate achieved in the sweep can be as low +# as 6 thousand sats/kilo-vbyte up to 14 thousand sats/kilo-vbyte. +# +# If this is not achieved, the transaction is aborted. For tumbler, +# it will then be retried until successful. +# WARNING: too-strict setting may result in using up a lot +# of PoDLE commitments, hence the default 0.8 (80%). +max_sweep_fee_change = 0.8 + +# Maximum absolute coinjoin fee in satoshi to pay to a single +# market maker for a transaction. Both the limits given in +# max_cj_fee_abs and max_cj_fee_rel must be exceeded in order +# to not consider a certain offer. +max_cj_fee_abs = 300000 + +# Maximum relative coinjoin fee, in fractions of the coinjoin value +# e.g. if your coinjoin amount is 2 btc (200 million satoshi) and +# max_cj_fee_rel = 0.001 (0.1%), the maximum fee allowed would +# be 0.002 btc (200 thousand satoshi) +max_cj_fee_rel = 0.0003 + +# The range of confirmations passed to the `listunspent` bitcoind RPC call +# 1st value is the inclusive minimum, defaults to one confirmation +# 2nd value is the exclusive maximum, defaults to most-positive-bignum (Google Me!) +# leaving it unset or empty defers to bitcoind's default values, ie [1, 9999999] +#listunspent_args = [] +# That's what you should do, unless you have a specific reason, eg: +# !!! WARNING !!! CONFIGURING THIS WHILE TAKING LIQUIDITY FROM +# !!! WARNING !!! THE PUBLIC ORDERBOOK LEAKS YOUR INPUT MERGES +# spend from unconfirmed transactions: listunspent_args = [0] +# display only unconfirmed transactions: listunspent_args = [0, 1] +# defend against small reorganizations: listunspent_args = [3] +# who is at risk of reorganization?: listunspent_args = [0, 2] +# NB: using 0 for the 1st value with scripts other than wallet-tool could cause +# spends from unconfirmed inputs, which may then get malleated or double-spent! +# other counterparties are likely to reject unconfirmed inputs... don't do it. + +# tx_broadcast: options: self, random-peer, not-self. +# +# self = broadcast transaction with your own bitcoin node. +# +# random-peer = everyone who took part in the coinjoin has a chance of broadcasting +# Note: if your counterparties do not support it, you will fall back +# to broadcasting via your own node. +# +# not-self = never broadcast with your own bitcoin node. +# +# Note: in this case if your counterparties do not broadcast for you, you +# will have to broadcast the tx manually (you can take the tx hex from the log +# or terminal) via some other channel. It is not recommended to choose this +# option when running schedules/tumbler. +tx_broadcast = random-peer + +# If makers do not respond while creating a coinjoin transaction, +# the non-responding ones will be ignored. This is the minimum +# amount of makers which we are content with for the coinjoin to +# succeed. Less makers means that the whole process will restart +# after a timeout. +minimum_makers = 4 + +# Threshold number of satoshis below which an incoming utxo +# to a reused address in the wallet will be AUTOMATICALLY frozen. +# This avoids forced address reuse attacks; see: +# https://en.bitcoin.it/wiki/Privacy#Forced_address_reuse +# +# The default is to ALWAYS freeze a utxo to an already used address, +# whatever the value of it, and this is set with the value -1. +max_sats_freeze_reuse = -1 + +# Interest rate used when calculating the value of fidelity bonds created +# by locking bitcoins in timelocked addresses +# See also: +# https://gist.github.com/chris-belcher/87ebbcbb639686057a389acb9ab3e25b#determining-interest-rate-r +# Set as a real number, i.e. 1 = 100% and 0.01 = 1% +interest_rate = 0.015 + +# Some makers run their bots to mix their funds not just to earn money +# So to improve privacy very slightly takers dont always choose a maker based +# on his fidelity bond but allow a certain small percentage to be chosen completely +# randomly without taking into account fidelity bonds +# This parameter sets how many makers on average will be chosen regardless of bonds +# A real number, i.e. 1 = 100%, 0.125 = 1/8 = 1 in every 8 makers on average will be bondless +bondless_makers_allowance = 0.125 + +# To (strongly) disincentivize Sybil behaviour, the value assessment of the bond +# is based on the (time value of the bond)^x where x is the bond_value_exponent here, +# where x > 1. It is a real number (so written as a decimal). +bond_value_exponent = 1.3 + +############################## +# THE FOLLOWING SETTINGS ARE REQUIRED TO DEFEND AGAINST SNOOPERS. +# DON'T ALTER THEM UNLESS YOU UNDERSTAND THE IMPLICATIONS. +############################## + +# Number of retries allowed for a specific utxo, to prevent DOS/snooping. +# Lower settings make snooping more expensive, but also prevent honest users +# from retrying if an error occurs. +taker_utxo_retries = 3 + +# Number of confirmations required for the commitment utxo mentioned above. +# this effectively rate-limits a snooper. +taker_utxo_age = 5 + +# Percentage of coinjoin amount that the commitment utxo must have +# as a minimum BTC amount. Thus 20 means a 1BTC coinjoin requires the +# utxo to be at least 0.2 btc. +taker_utxo_amtpercent = 20 + +# Set to 1 to accept broadcast PoDLE commitments from other bots, and +# add them to your blacklist (only relevant for Makers). +# There is no way to spoof these values, so the only "risk" is that +# someone fills your blacklist file with a lot of data. +accept_commitment_broadcasts = 1 + +# Location of your commitments.json file (stores commitments you've used +# and those you want to use in future), relative to the scripts directory. +commit_file_location = cmtdata/commitments.json + +# Location of the file used by makers to keep track of used/blacklisted +# commitments. For remote daemon, set to `.` to have it stored locally +# (but note that *all* bots using the same code installation share it, +# in this case, which can be bad in testing). +commitment_list_location = cmtdata/commitmentlist + +############################## +# END OF ANTI-SNOOPING SETTINGS +############################## + +[PAYJOIN] +# For the majority of situations, the defaults +# need not be altered - they will ensure you don't pay +# a significantly higher fee. +# MODIFICATION OF THESE SETTINGS IS DISADVISED. + +# Payjoin protocol version; currently only '1' is supported. +payjoin_version = 1 + +# Servers can change their destination address by default (0). +# if '1', they cannot. Note that servers can explicitly request +# that this is activated, in which case we respect that choice. +disable_output_substitution = 0 + +# "default" here indicates that we will allow the receiver to +# increase the fee we pay by: +# 1.2 * (our_fee_rate_per_vbyte * vsize_of_our_input_type) +# (see https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#span_idfeeoutputspanFee_output) +# (and 1.2 to give breathing room) +# which indicates we are allowing roughly one extra input's fee. +# If it is instead set to an integer, then that many satoshis are allowed. +# Additionally, note that we will also set the parameter additionafeeoutputindex +# to that of our change output, unless there is none in which case this is disabled. +max_additional_fee_contribution = default + +# This is the minimum sats/vbyte we allow in the payjoin +# transaction; note it is decimal, not integer. +min_fee_rate = 1.1 + +# For payjoins as sender (i.e. client) to hidden service endpoints, +# the socks5 configuration: +onion_socks5_host = localhost +onion_socks5_port = 9050 + +# For payjoin onion service creation: +# the tor control configuration: +tor_control_host = localhost + +# or, to use a UNIX socket +# control_host = unix:/var/run/tor/control +# note: port needs to be provided (but is ignored for UNIX socket) +tor_control_port = 9051 + +# the host/port actually serving the hidden service +# (note the *virtual port*, that the client uses, +# is hardcoded to 80): +onion_serving_host = 127.0.0.1 +onion_serving_port = 8080 + +# in some exceptional case the HS may be SSL configured, +# this feature is not yet implemented in code, but here for the +# future: +hidden_service_ssl = false + +[YIELDGENERATOR] +# [string, 'reloffer' or 'absoffer'], which fee type to actually use +ordertype = reloffer + +# [satoshis, any integer] / absolute offer fee you wish to receive for coinjoins (cj) +cjfee_a = 500 + +# [fraction, any str between 0-1] / relative offer fee you wish to receive based on a cj's amount +cjfee_r = 0.00002 + +# [fraction, 0-1] / variance around the average fee. Ex: 200 fee, 0.2 var = fee is btw 160-240 +cjfee_factor = 0.1 + +# [satoshis, any integer] / the average transaction fee you're adding to coinjoin transactions +# (note: this will soon be deprecated; leave at zero) +txfee_contribution = 0 + +# [fraction, 0-1] / variance around the average fee. Ex: 1000 fee, 0.2 var = fee is btw 800-1200 +txfee_contribution_factor = 0.3 + +# [satoshis, any integer] / minimum size of your cj offer. Lower cj amounts will be disregarded +minsize = 100000 + +# [fraction, 0-1] / variance around all offer sizes. Ex: 500k minsize, 0.1 var = 450k-550k +size_factor = 0.1 + +gaplimit = 6 + +[SNICKER] +# Any other value than 'true' will be treated as False, +# and no SNICKER actions will be enabled in that case: +enabled = false + +# In satoshis, we require any SNICKER to pay us at least +# this much (can be negative), otherwise we will refuse +# to sign it: +lowest_net_gain = 0 + +# Comma separated list of servers (if port is omitted as :port, it +# is assumed to be 80) which we will poll against (all, in sequence); note +# that they are allowed to be *.onion or cleartext servers, and no +# scheme (http(s) etc) needs to be added to the start. +servers = cn5lfwvrswicuxn3gjsxoved6l2gu5hdvwy5l3ev7kg6j7lbji2k7hqd.onion, + +# How many minutes between each polling event to each server above: +polling_interval_minutes = 60 diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh new file mode 100755 index 0000000..052a661 --- /dev/null +++ b/docker_entrypoint.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +set -e + +_term() { + echo "Caught SIGTERM signal!" + kill -TERM "$jm_child" 2>/dev/null + exit 0 +} + +# Setting env-vars +echo "Setting environment variables..." +export APP_USER=$(yq e '.username' /root/start9/config.yaml) +export APP_PASSWORD=$(yq e '.password' /root/start9/config.yaml) +export TOR_HOST=$(yq e '.tor-address' /root/start9/config.yaml) +export LAN_HOST=$(yq e '.lan-address' /root/start9/config.yaml) +export RPC_TYPE=$(yq e '.bitcoind.type' /root/start9/config.yaml) +export JM_RPC_USER=$(yq e '.bitcoind.user' /root/start9/config.yaml) +export JM_RPC_PASSWORD=$(yq e '.bitcoind.password' /root/start9/config.yaml) +export RPC_PORT=8332 +export JM_WALLET=$(yq e '.jm-wallet' /root/start9/config.yaml) +export JM_HOST="jam.embassy" +if [ "$RPC_TYPE" = "internal-proxy" ]; then + export JM_RPC_HOST="btc-rpc-proxy.embassy" + echo "Running on Bitcoin Proxy..." +else + export JM_RPC_HOST="bitcoind.embassy" + echo "Running on Bitcoin Core..." +fi + +# Configuring Webserver +echo "Configuring JoinMarket..." +sed -i "s/\[supervisord\].*/\[supervisord\]\nuser=root/" /etc/supervisor/supervisord.conf +echo '' >> /etc/supervisor/supervisord.conf && echo '[inet_http_server]' >> /etc/supervisor/supervisord.conf && echo 'port = *:8080' >> /etc/supervisor/supervisord.conf +if ! [ -f "/root/.joinmarket/joinmarket.cfg" ]; then + echo "Creating JM configuration file..." + cd /src/scripts/ + python jmwalletd.py +fi +sed -i "s/rpc_host =.*/rpc_host = $RPC_HOST/" /root/.joinmarket/joinmarket.cfg +sed -i "s/rpc_password =.*/rpc_password = $JM_RPC_PASSWORD/" /root/.joinmarket/joinmarket.cfg +sed -i "s/rpc_user =.*/rpc_user = $JM_RPC_USER/" /root/.joinmarket/joinmarket.cfg +sed -i "s/rpc_port =.*/rpc_port = $RPC_PORT/" /root/.joinmarket/joinmarket.cfg +sed -i "s/rpc_wallet_file =.*/rpc_wallet_file = $JM_WALLET/" /root/.joinmarket/joinmarket.cfg +sed -i "s/localhost/$JM_HOST/" /root/.joinmarket/joinmarket.cfg +sed -i "s/'jm_webui_default'/$JM_WALLET/g" /jam-entrypoint.sh +sed -i "s/exec supervisord.*/exec supervisord -c \/etc\/supervisor\/supervisord\.conf/" /jam-entrypoint.sh + +# Properties Page showing password to be used for login + echo 'version: 2' > /root/start9/stats.yaml + echo 'data:' >> /root/start9/stats.yaml + echo ' Username: ' >> /root/start9/stats.yaml + echo ' type: string' >> /root/start9/stats.yaml + echo " value: \"$APP_USER\"" >> /root/start9/stats.yaml + echo ' description: This is your username for JAM' >> /root/start9/stats.yaml + echo ' copyable: true' >> /root/start9/stats.yaml + echo ' masked: false' >> /root/start9/stats.yaml + echo ' qr: false' >> /root/start9/stats.yaml + echo ' Password: ' >> /root/start9/stats.yaml + echo ' type: string' >> /root/start9/stats.yaml + echo " value: \"$APP_PASSWORD\"" >> /root/start9/stats.yaml + echo ' description: This is your password for JAM. Please use caution when sharing this password, you could lose your funds!' >> /root/start9/stats.yaml + echo ' copyable: true' >> /root/start9/stats.yaml + echo ' masked: true' >> /root/start9/stats.yaml + echo ' qr: false' >> /root/start9/stats.yaml + +export count=`find /root/.joinmarket/wallets -type f -iname ".*.jmdat.lock" | wc -l` +if [ $count != 0 ] +then + echo "Unlocking JAM Wallet(s)..." + rm -rf /root/.joinmarket/wallets/.*.jmdat.lock +fi + +# Starting JoinMarket API +echo "Starting JoinMarket..." +cd /src/scripts/ +/jam-entrypoint.sh + +trap _term SIGTERM +wait $jm_child \ No newline at end of file diff --git a/docs/instructions.md b/docs/instructions.md new file mode 100644 index 0000000..817593e --- /dev/null +++ b/docs/instructions.md @@ -0,0 +1,11 @@ +# Welcome to JAM! + +JoinMarket is software to create a special kind of bitcoin transaction called a CoinJoin transaction. Its aim is to improve the confidentiality and privacy of bitcoin transactions. + +A CoinJoin transaction requires other people to take part. The right resources (coins) have to be in the right place, at the right time, in the right quantity. This isn't a software or tech problem, it's an economic problem. JoinMarket works by creating a new kind of market that would allocate these resources in the best way. + +One group of participants (called market makers) will always be available to take part in CoinJoins at any time. Other participants (called market takers) can create a CoinJoin at any time. The takers pay a fee which incentivizes the makers. A form of smart contract is created, meaning the private keys will never be broadcasted outside of your computer, resulting in virtually zero risk of loss (aside from malware or bugs). As a result of free-market forces the fees will eventually be next to nothing. + +Widespread use of JoinMarket improves bitcoin's fungibility and privacy. This implementation of JoinMarket also implements [PayJoin](https://en.bitcoin.it/wiki/PayJoin). + +For a quick introduction to Joinmarket you can watch [this demonstration](https://youtu.be/hwmvZVQ4C4M) of installation and usage given by [Adam Gibson](https://github.com/AdamISZ) during the [Understanding Bitcoin conference](https://understandingbtc.com/) on April 6 2019. \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..acb16de Binary files /dev/null and b/icon.png differ diff --git a/jam-docker b/jam-docker new file mode 160000 index 0000000..4d4b8fd --- /dev/null +++ b/jam-docker @@ -0,0 +1 @@ +Subproject commit 4d4b8fd9336e7174a95fc22e6e1a79cf87439597 diff --git a/manifest.yaml b/manifest.yaml new file mode 100644 index 0000000..2a2fa13 --- /dev/null +++ b/manifest.yaml @@ -0,0 +1,158 @@ +id: jam +title: JAM +version: 0.0.10 +release-notes: Initial release to EmbassyOS +license: mit +wrapper-repo: "https://github.com/cryptodread/jam-wrapper" +upstream-repo: "https://github.com/jam/jam-docker" +support-site: "https://github.com/jam/jam-docker/issues" +marketing-site: "https://t.me/JoinMarketWebUI" +build: ["make"] +description: + short: JAM - A friendly UI for JoinMarket + long: | + Jam is a web UI for JoinMarket with focus on user-friendliness. + It aims to provide sensible defaults and be easy to use for beginners while still providing the features advanced users expect. +assets: + license: LICENSE + icon: icon.png + instructions: docs/instructions.md + docker-images: image.tar +main: + type: docker + image: main + entrypoint: "docker_entrypoint.sh" + args: [] + mounts: + main: /root +health-checks: + main: + name: JAM UI + success-message: JAM is ready to visit in a web browser + type: docker + image: main + entrypoint: "check-web.sh" + args: [] + system: false + io-format: json + api: + name: JAM API + success-message: The JAM API is accessible and responding + type: docker + image: main + entrypoint: "check-api.sh" + args: [] + system: false + io-format: json +config: + get: + type: script + set: + type: script +properties: + type: script +volumes: + main: + type: data + compat: + type: assets +interfaces: + main: + name: JAM Web UI + description: Specifies the interface to listen on for HTTP connections. + tor-config: + port-mapping: + 80: "80" + 28183: "28183" + 27183: "27183" + lan-config: + 443: + ssl: true + internal: 80 + 28183: + ssl: true + internal: 28183 + 27183: + ssl: true + internal: 27183 + ui: true + protocols: + - tcp + - http +dependencies: + bitcoind: + version: ">=0.21.1.2 <=23.0.0" + requirement: + type: "opt-in" + how: Can alternatively configure proxy or bitcoin core node. + description: Used to subscribe to new block events from a full archival node. + config: + check: + type: script + auto-configure: + type: script + btc-rpc-proxy: + version: ">=0.3.2.1 <0.4.0" + requirement: + type: "opt-out" + how: Can alternatively configure proxy or bitcoin core node. + description: Used for RPC permissioning. + config: + check: + type: script + auto-configure: + type: script +alerts: + start: | + ⚠️ WARNING ⚠️ This is ALPHA software. Use with caution. +migrations: + from: + "*": + type: script + args: ["from"] + to: + "*": + type: script + args: ["to"] +backup: + create: + type: docker + image: compat + system: true + entrypoint: compat + args: + - duplicity + - create + - /mnt/backup + - /root/data + mounts: + BACKUP: "/mnt/backup" + main: "/root/data" + restore: + type: docker + image: compat + system: true + entrypoint: compat + args: + - duplicity + - restore + - /root/data + - /mnt/backup + mounts: + BACKUP: "/mnt/backup" + main: "/root/data" +actions: + unlock-wallet: + name: Unlock Wallet + description: Unlocks wallet after JAM restarts + warning: Make sure you have your seed phrase backed up properly! + allowed-statuses: + - running + implementation: + type: docker + image: main + system: false + entrypoint: unlock-wallet.sh + args: [] + inject: true + io-format: json diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..4c43fe6 --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +*.js \ No newline at end of file diff --git a/scripts/deps.ts b/scripts/deps.ts new file mode 100644 index 0000000..090054d --- /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..b4eab1c --- /dev/null +++ b/scripts/embassy.ts @@ -0,0 +1,5 @@ +export { setConfig } from "./services/setConfig.ts"; +export { properties } from "./services/properties.ts"; +export { getConfig } from "./services/getConfig.ts"; +export { dependencies } from "./services/dependencies.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..52588c0 --- /dev/null +++ b/scripts/services/dependencies.ts @@ -0,0 +1,156 @@ +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, + }, + ) + ), +}); + +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 = "jam"; +const fullChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +type Check = { + currentError(config: T.Config): string | void; + fix(config: T.Config): void; +}; + +const checks: 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(""), + }); + }, + }, + ...[ + "createwallet", + "loadwallet", + "listwallets", + "getwalletinfo", + "getblockcount", + "listaddressgroupings", + "listunspent", + "listtransactions", + "importmulti", + "listlabels", + "getaddressesbylabel", + "getinfo", + "getbestblockhash", + "gettxout", + "getblockchaininfo", + "sendrawtransaction", + "getblockhash", + "getblock", + "getblockheader", + "estimatesmartfee", + "getnetworkinfo", + "uptime", + "getrawtransaction", + "getpeerinfo", + "getmempoolinfo", + "getzmqnotifications", + ].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(`Users for "${serviceName}" should exist`); + } + found["allowed-calls"] = [...(found["allowed-calls"] ?? []), operator]; + }, + }) + ), +]; + +const matchBitcoindConfig = shape({ + "rpc.enable": boolean +}); + +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 checks) { + 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 checks) { + const error = checker.currentError(configInput); + if (error) { + checker.fix(configInput); + } + } + return { result: configInput }; + }, + }, + bitcoind: { + // deno-lint-ignore require-await + async check(_effects, configInput) { + const config = matchBitcoindConfig.unsafeCast(configInput); + if (!config['rpc.enable']) { + return { error: 'Must have RPC enabled' }; + } + return { result: null }; + }, + // deno-lint-ignore require-await + async autoConfigure(_effects, configInput) { + const config = matchBitcoindConfig.unsafeCast(configInput); + config['rpc.enable'] = true; + return { result: config }; + }, + }, +}; diff --git a/scripts/services/getConfig.ts b/scripts/services/getConfig.ts new file mode 100644 index 0000000..42c3639 --- /dev/null +++ b/scripts/services/getConfig.ts @@ -0,0 +1,111 @@ +import { compat, types as T } from "../deps.ts"; + +export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ + "tor-address": { + "name": "Tor Address", + "description": "The Tor address of the network interface", + "type": "pointer", + "subtype": "package", + "package-id": "jam", + "target": "tor-address", + "interface": "main", + }, + "lan-address": { + "name": "Network LAN Address", + "description": "The LAN address for the network interface.", + "type": "pointer", + "subtype": "package", + "package-id": "jam", + "target": "lan-address", + "interface": "main" + }, + "username": { + "type": "string", + "name": "JAM Username", + "description": "Administrator password for Lightning Terminal", + "nullable": false, + "copyable": true, + "masked": false, + "default": "embassy" + }, + "password": { + "type": "string", + "name": "JAM Password", + "description": "Administrator password for Lightning Terminal", + "nullable": false, + "copyable": true, + "masked": true, + "default": { + "charset": "a-z,A-Z,0-9", + "len": 22 + } + }, + "jm-wallet": { + "type": "string", + "name": "JAM Bitcoin Wallet", + "description": "Bitcoin Core Wallet created for JAM", + "default": "jam_wallet", + "nullable": false + }, + "bitcoind": { + "type": "union", + "name": "Bitcoin Core", + "description": "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core or Proxy services installed to your Embassy\n", + "tag": { + "id": "type", + "name": "Type", + "variant-names": { + "internal": "Bitcoin Core", + "internal-proxy": "Bitcoin Proxy" + }, + "description": "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core and Proxy services installed to your Embassy\n" + }, + "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 JAM", + "subtype": "package", + "package-id": "btc-rpc-proxy", + "target": "config", + "multi": false, + "selector": "$.users[?(@.name == \"jam\")].name" + }, + "password": { + "type": "pointer", + "name": "RPC Password", + "description": "The password for the RPC user allocated to JAM", + "subtype": "package", + "package-id": "btc-rpc-proxy", + "target": "config", + "multi": false, + "selector": "$.users[?(@.name == \"jam\")].password" + }, + } + } + } +}); diff --git a/scripts/services/migrations.ts b/scripts/services/migrations.ts new file mode 100644 index 0000000..f86bd48 --- /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.0.10" ); diff --git a/scripts/services/properties.ts b/scripts/services/properties.ts new file mode 100644 index 0000000..c11acba --- /dev/null +++ b/scripts/services/properties.ts @@ -0,0 +1,3 @@ +import { compat, types as T } from "../deps.ts"; + +export const properties: T.ExpectedExports.properties = compat.properties; \ No newline at end of file diff --git a/scripts/services/setConfig.ts b/scripts/services/setConfig.ts new file mode 100644 index 0000000..7dc9aef --- /dev/null +++ b/scripts/services/setConfig.ts @@ -0,0 +1,4 @@ +import { + compat, +} from "../deps.ts"; +export const setConfig = compat.setConfig;