diff --git a/Dockerfile b/Dockerfile index 1afe884..c0023f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM nicolasdorier/nbxplorer:2.5.9 AS nbx-builder +FROM btcpayserver/monero:0.18.3.4 AS monero-wallet-rpc +FROM nicolasdorier/nbxplorer:2.5.12 AS nbx-builder FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim AS actions-builder ARG TARGETARCH @@ -8,10 +9,11 @@ RUN dotnet restore "utils/actions/actions.csproj" -a $TARGETARCH WORKDIR "/actions" RUN dotnet build "utils/actions/actions.csproj" -c Release -a $TARGETARCH -o /actions/build -FROM btcpayserver/btcpayserver:2.0.0 +FROM btcpayserver/btcpayserver:2.0.3-altcoins COPY --from=nbx-builder "/app" /nbxplorer COPY --from=actions-builder "/actions/build" /actions +COPY --from=monero-wallet-rpc "/usr/local/bin/monero-wallet-rpc" /usr/local/bin/ # arm64 or amd64 ARG PLATFORM @@ -19,13 +21,19 @@ ARG PLATFORM ARG ARCH # install package dependencies -RUN apt-get update && \ - apt-get install -y sqlite3 libsqlite3-0 curl locales jq bc wget procps postgresql-common xz-utils nginx vim && \ - curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc && \ - sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list' && \ - apt-get update && apt-get install -y postgresql-13 && \ - wget https://github.com/mikefarah/yq/releases/download/v4.6.3/yq_linux_${PLATFORM}.tar.gz -O - |\ - tar xz && mv yq_linux_${PLATFORM} /usr/bin/yq +RUN sed -i "s|http://|https://|g" /etc/apt/sources.list.d/debian.sources +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y sqlite3 libsqlite3-0 curl locales jq bc wget procps xz-utils nginx vim \ + && mkdir -p /usr/share/postgresql-common/pgdg \ + && curl -so /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + && sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \ + && apt-get update && apt-get install -y postgresql-13 \ + && wget https://github.com/mikefarah/yq/releases/download/v4.6.3/yq_linux_${PLATFORM}.tar.gz -O - |\ + tar xz && mv yq_linux_${PLATFORM} /usr/bin/yq \ + && apt-get -y autoremove \ + && apt-get clean autoclean \ + && rm -rf /var/lib/apt/lists/* # install S6 overlay for proces mgmt # https://github.com/just-containers/s6-overlay @@ -61,13 +69,16 @@ ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ BTCPAY_PLUGINDIR=/datadir/plugins # postgres setup -RUN groupadd -r postgres --gid=999; \ - useradd -r -g postgres --gid=999 --home-dir=/var/lib/postgresql --shell=/bin/bash postgres; \ - mkdir -p /var/lib/postgresql; \ +#RUN useradd -r -g postgres --home-dir=/var/lib/postgresql --shell=/bin/bash postgres; \ +RUN mkdir -p /var/lib/postgresql; \ chown -R postgres:postgres /var/lib/postgresql; \ mkdir -p /var/run/postgresql; \ chown -R postgres:postgres /var/run/postgresql; \ - chmod 2777 /var/run/postgresql; + chmod 2770 /var/run/postgresql; + +# monero setup +RUN groupadd -r monero --gid=302340; \ + useradd -r -g monero --uid=30236 --gid=302340 -M --home-dir=/dev/null --shell=/sbin/nologin monero # project specific postgres env vars ENV POSTGRES_HOST_AUTH_METHOD=trust \ @@ -79,11 +90,13 @@ ENV POSTGRES_HOST_AUTH_METHOD=trust \ ADD ./configurator/target/${ARCH}-unknown-linux-musl/release/configurator /usr/local/bin/configurator COPY utils/scripts/btcpay-admin.sh /usr/local/bin/btcpay-admin.sh COPY utils/scripts/health_check.sh /usr/local/bin/health_check.sh -COPY utils/nginx.conf /etc/nginx/sites-available/default +COPY utils/config/nginx.conf /etc/nginx/sites-available/default +COPY utils/config/monero-wallet-rpc.btcpayserver.conf.template /etc/ COPY utils/scripts/postgres-init.sh /etc/s6-overlay/script/postgres-init COPY utils/scripts/postgres-ready.sh /etc/s6-overlay/script/postgres-ready COPY utils/scripts/postgres-shutdown.sh /etc/cont-finish.d/postgres-shutdown -RUN chmod a+x /usr/local/bin/btcpay-admin.sh /usr/local/bin/health_check.sh /etc/s6-overlay/script/* /etc/cont-finish.d/* +COPY utils/scripts/notifier.sh /usr/local/bin/notifier.sh +RUN chmod a+x /usr/local/bin/btcpay-admin.sh /usr/local/bin/health_check.sh /etc/s6-overlay/script/* /etc/cont-finish.d/* /usr/local/bin/notifier.sh # s6-overlay initialization ENTRYPOINT ["/init"] \ No newline at end of file diff --git a/Makefile b/Makefile index 201bc43..0110efa 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ verify: $(PKG_ID).s9pk @echo " Done!" @echo " Filesize: $(shell du -h $(PKG_ID).s9pk) is ready" -install: +install: $(PKG_ID).s9pk ifeq (,$(wildcard ~/.embassy/config.yaml)) @echo; echo "You must define \"host: http://server-name.local\" in ~/.embassy/config.yaml config file first"; echo else diff --git a/configurator/src/main.rs b/configurator/src/main.rs index d5ea2ce..47fff17 100644 --- a/configurator/src/main.rs +++ b/configurator/src/main.rs @@ -13,6 +13,27 @@ struct Config { lightning: LightningConfig, bitcoin_rpc_user: String, bitcoin_rpc_password: String, + altcoins: AltcoinConfig, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct MoneroStart9Config { + rpc: MoneroRpcCredentials, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct MoneroRpcCredentials { + rpc_credentials: MoneroRpcCredentialValues, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct MoneroRpcCredentialValues { + enabled: Status, + password: Option, + username: Option, } #[derive(serde::Deserialize)] @@ -26,6 +47,28 @@ enum LightningConfig { #[serde(rename_all = "kebab-case")] CLightning, } + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +enum Status { + #[serde(rename_all = "lowercase")] + Enabled, + #[serde(rename_all = "lowercase")] + Disabled, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct MoneroConfig { + status: Status, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct AltcoinConfig { + monero: MoneroConfig, +} + #[derive(serde::Serialize)] pub struct Property { #[serde(rename = "type")] @@ -43,6 +86,10 @@ fn main() -> Result<(), anyhow::Error> { let config: Config = serde_yaml::from_reader( File::open("/datadir/start9/config.yaml").with_context(|| "/datadir/start9/config.yaml")?, )?; + let monero_config: MoneroStart9Config = serde_yaml::from_reader( + File::open("/mnt/monerod/start9/config.yaml") + .with_context(|| "/mnt/monerod/start9/config.yaml")?, + )?; let tor_address = config.tor_address; let mut nbx_config = File::create("/datadir/nbxplorer/Main/settings.config") .with_context(|| "/datadir/nbxplorer/mainnet/settings.config")?; @@ -60,10 +107,41 @@ fn main() -> Result<(), anyhow::Error> { p2p_host = bitcoin_host, p2p_port = 8333 )?; - write!( - btcpay_config, - include_str!("templates/settings-btcpay.config.template"), - )?; + + match config.altcoins.monero.status { + Status::Enabled => { + match monero_config.rpc.rpc_credentials.enabled { + Status::Enabled => { + write!( + btcpay_config, + include_str!("templates/settings-btcpay.config.template"), + monero_username = &monero_config.rpc.rpc_credentials.username.unwrap(), + monero_password = &monero_config.rpc.rpc_credentials.password.unwrap(), + chains = "btc,xmr" + )?; + } + Status::Disabled => { + write!( + btcpay_config, + include_str!("templates/settings-btcpay.config.template"), + monero_username = "", + monero_password = "", + chains = "btc,xmr" + )?; + } + } + println!("{}", format!("export BTCPAYGEN_CRYPTO2='xmr'\n")); + } + Status::Disabled => { + write!( + btcpay_config, + include_str!("templates/settings-btcpay.config.template"), + monero_username = "", + monero_password = "", + chains = "btc" + )?; + } + } let addr = tor_address.split('.').collect::>(); match addr.first() { diff --git a/configurator/src/templates/settings-btcpay.config.template b/configurator/src/templates/settings-btcpay.config.template index 3b5d9e3..bd007a8 100644 --- a/configurator/src/templates/settings-btcpay.config.template +++ b/configurator/src/templates/settings-btcpay.config.template @@ -1,5 +1,6 @@ ### Global settings ### network=mainnet +chains={chains} ### Server settings ### port=23000 @@ -16,4 +17,12 @@ sqlitefile=sqlite.db #BTC.explorer.url=http://127.0.0.1:24444/ BTC.explorer.cookiefile=/datadir/nbxplorer/Main/.cookie #BTC.lightning=/root/.lightning/lightning-rpc -#BTC.lightning=https://apitoken:API_TOKEN_SECRET@charge.example.com/ \ No newline at end of file +#BTC.lightning=https://apitoken:API_TOKEN_SECRET@charge.example.com/ + +### Monero settings ### +XMR_daemon_uri=http://monerod.embassy:18089 +XMR_wallet_daemon_uri=http://127.0.0.1:18082 +XMR_wallet_daemon_walletdir=/datadir/btcpayserver/altcoins/monero/wallets +# Only use username and password if Monero daemon's RPC is password protected +XMR_daemon_username={monero_username} +XMR_daemon_password={monero_password} \ No newline at end of file diff --git a/docs/instructions.md b/docs/instructions.md index ea5ae34..1147437 100644 --- a/docs/instructions.md +++ b/docs/instructions.md @@ -62,6 +62,29 @@ Next, **enable lightning** for a particular store's wallet using the connection If you would like to connect to an external lightning node, select "Use custom node" when on the page above and follow the instructions. +## Enabling Altcoins + +In config, find `Altcoin Integrations` and set the desired altcoin implemtation to "Enabled". + +### Monero + +This service can be installed on your server from the Community Marketplace and enabled in config by setting `Altcoin Integrations > Monero` to "Enabled". You will need a fully synced Monero node for operations to work properly. + +#### Wallet + +Enabling Monero requires a manual wallet setup. Fortunately, the community has put together some great guides to do so: + +- [SethForPrivacy Guide](https://sethforprivacy.com/guides/accepting-monero-via-btcpay-server/#setup-your-bitcoin-and-monero-wallets) +- [Freedom Node Guide](https://freedomnode.com/blog/howto-accept-monero-for-your-services-btcpayserver/#create-a-view-only-monero-wallet-with-feather) + +Using these guides, setup a Feather wallet (recommended), then proceed to upload the wallet to BTCPay Server for your store by opening the store settings and finding the "Monero" tab across the top menu bar. The Monero node will need to be fully synced before it is available to use in BTCPay Server. + +If you are unable to open Feather on macOS, go to System Settings > Privacy & Security and scroll to find where "Feather" was blocked becuase it is not from an identified developer > "Open Anyway" > Enter password > Select "Open" regardless of security warning. + +#### RPC + +If you enable / disable RPC credentials in Monero, you will need to restart BTC Pay Server to pick up this change. + ## Setting up BTCPayServer Vault BTCPayServer Vault supports hardware wallet integrations for stores. To use, you must [install Vault](https://github.com/btcpayserver/BTCPayServer.Vault/releases) and run it on a laptop/desktop machine. Then, access your BTCPay Server service in a browser on the same device. diff --git a/manifest.yaml b/manifest.yaml index 2df0645..f23f275 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,9 +1,11 @@ id: btcpayserver title: BTCPay Server -version: 2.0.0 +version: 2.0.3 release-notes: | - * Major upstream release of [v2.0.0](https://github.com/btcpayserver/btcpayserver/releases/tag/v2.0.0)! This release introduces many new features and some breaking changes. Please see the [blog post](https://blog.btcpayserver.org/btcpay-server-2-0/) for more details. + * Updates to the latest upstream [release](https://github.com/btcpayserver/btcpayserver/releases/tag/v2.0.3) + * Includes major upstream release of [v2.0.0](https://github.com/btcpayserver/btcpayserver/releases/tag/v2.0.0)! This release introduces many new features and some breaking changes. Please see the [blog post](https://blog.btcpayserver.org/btcpay-server-2-0/) for more details. * Please note: large instances may experience a few minutes of database migration. + * Adds optional Monero integration for payment processing license: mit wrapper-repo: "https://github.com/Start9Labs/btcpayserver-wrapper" upstream-repo: "https://github.com/btcpayserver/btcpayserver" @@ -29,6 +31,7 @@ main: mounts: lnd: /mnt/lnd c-lightning: "/mnt/c-lightning" + monerod: "/mnt/monerod" main: /datadir health-checks: explorer-synced: @@ -88,6 +91,17 @@ dependencies: how: Optionally use Core Lightning for internal lightning network node implementation. description: Used to communicate with the Lightning Network. config: ~ + monerod: + version: ">=0.18.3.2 <0.20.0" + requirement: + type: "opt-in" + how: Optionally use Monero. + description: Used as an alternative integration for payment processing. + config: + check: + type: script + auto-configure: + type: script volumes: main: type: data @@ -103,6 +117,12 @@ volumes: volume-id: main path: "/shared" readonly: true + monerod: + type: pointer + package-id: monerod + volume-id: main + path: "/" + readonly: false alerts: install:

BTCPay is a self hosted payment processing system. No third party exists to backup data. You are responsible for backups of all your information!

diff --git a/scripts/migrations/2_0_0_up_migration.ts b/scripts/migrations/2_0_0_up_migration.ts new file mode 100644 index 0000000..2561ce6 --- /dev/null +++ b/scripts/migrations/2_0_0_up_migration.ts @@ -0,0 +1,34 @@ +import { types as T, matches } from "../deps.ts"; + +const { shape, string } = matches; + +export const migration_up_2_0_0 = (config: T.Config): T.Config => { + if (Object.keys(config).length === 0) { + // service was never configured + return config; + } + + const altcoinsConfig = shape({ + monero: shape({ + status: string, + }), + }); + + const matchConfigWithAltcoins = shape({ + altcoins: altcoinsConfig, + }); + + if (!matchConfigWithAltcoins.test(config)) { + const newAltcoinsConfig: typeof altcoinsConfig._TYPE = { + monero: { + status: "disabled", + }, + }; + return { + ...config, + altcoins: newAltcoinsConfig, + }; + } else { + return config; + } +}; diff --git a/scripts/procedures/dependencies.ts b/scripts/procedures/dependencies.ts index 59aa0e2..246a7b9 100644 --- a/scripts/procedures/dependencies.ts +++ b/scripts/procedures/dependencies.ts @@ -10,12 +10,11 @@ type Check = { async function readConfig(effects: T.Effects): Promise { const configMatcher = dictionary([string, any]); const config = configMatcher.unsafeCast( - await effects.readFile( - { + await effects + .readFile({ path: "start9/config.yaml", volumeId: "main", - } - ) + }) .then(YAML.parse) ); return config; @@ -38,7 +37,7 @@ const matchBitcoindConfig = shape({ const matchOldBitcoindConfig = shape({ rpc: shape({ advanced: shape({ - serialversion: matches.any + serialversion: matches.any, }), }), advanced: shape({ @@ -46,8 +45,35 @@ const matchOldBitcoindConfig = shape({ mode: string, }), }), -}) +}); + +const matchMoneroConfig = shape({ + integrations: shape({ + blocknotify: shape({ + btcpayserver: boolean, + }), + }), +}); +const moneroChecks: Array = [ + { + currentError(config) { + if (!matchMoneroConfig.test(config)) { + return "Monero config is not the correct shape"; + } + if (!config.integrations.blocknotify.btcpayserver) { + return "Must have blocknotify enabled for btcpayserver"; + } + return; + }, + fix(config) { + if (!matchMoneroConfig.test(config)) { + return; + } + config.integrations.blocknotify.btcpayserver = true; + }, + }, +]; const bitcoindChecks: Array = [ { @@ -62,7 +88,7 @@ const bitcoindChecks: Array = [ }, fix(config) { if (!matchBitcoindConfig.test(config)) { - return + return; } config.rpc.enable = true; }, @@ -79,25 +105,28 @@ const bitcoindChecks: Array = [ }, fix(config) { if (!matchBitcoindConfig.test(config)) { - return + return; } config.advanced.peers.listen = true; }, }, { currentError(config) { - if (matchOldBitcoindConfig.test(config) && config.advanced.pruning.mode !== "disabled") { - return 'Pruning must be disabled to use with <= 24.0.1 of Bitcoin Core. To use with a pruned node, update Bitcoin Core to >= 25.0.0~2.'; + if ( + matchOldBitcoindConfig.test(config) && + config.advanced.pruning.mode !== "disabled" + ) { + return "Pruning must be disabled to use with <= 24.0.1 of Bitcoin Core. To use with a pruned node, update Bitcoin Core to >= 25.0.0~2."; } return; }, fix(config) { if (!matchOldBitcoindConfig.test(config)) { - return + return; } - config.advanced.pruning.mode = "disabled" + config.advanced.pruning.mode = "disabled"; }, - } + }, ]; export const dependencies: T.ExpectedExports.dependencies = { @@ -126,4 +155,29 @@ export const dependencies: T.ExpectedExports.dependencies = { return { result: bitcoindConfig }; }, }, + monerod: { + // deno-lint-ignore require-await + async check(effects, moneroConfig) { + effects.info("check monerod"); + for (const checker of moneroChecks) { + const error = checker.currentError(moneroConfig); + if (error) { + effects.error(`throwing error: ${error}`); + return { error }; + } + } + return { result: null }; + }, + // deno-lint-ignore require-await + async autoConfigure(effects, moneroConfig) { + effects.info("autoconfigure monerod"); + for (const checker of moneroChecks) { + const error = checker.currentError(moneroConfig); + if (error) { + checker.fix(moneroConfig); + } + } + return { result: moneroConfig }; + }, + }, }; diff --git a/scripts/procedures/getConfig.ts b/scripts/procedures/getConfig.ts index e7b90e2..4fc13b1 100644 --- a/scripts/procedures/getConfig.ts +++ b/scripts/procedures/getConfig.ts @@ -2,69 +2,99 @@ import { compat } from "../deps.ts"; export const getConfig = compat.getConfig({ "tor-address": { - "name": "Network Tor Address", - "description": "The Tor address for the network interface.", - "type": "pointer", - "subtype": "package", + name: "Network Tor Address", + description: "The Tor address for the network interface.", + type: "pointer", + subtype: "package", "package-id": "btcpayserver", - "target": "tor-address", - "interface": "main" + target: "tor-address", + interface: "main", }, "bitcoin-rpc-user": { - "type": "pointer", - "name": "Bitcoin RPC Username", - "description": "The username for Bitcoin Core's RPC interface", - "subtype": "package", + type: "pointer", + name: "Bitcoin RPC Username", + description: "The username for Bitcoin Core's RPC interface", + subtype: "package", "package-id": "bitcoind", - "target": "config", - "multi": false, - "selector": "$.rpc.username" + target: "config", + multi: false, + selector: "$.rpc.username", }, "bitcoin-rpc-password": { - "type": "pointer", - "name": "Bitcoin RPC Password", - "description": "The password for Bitcoin Core's RPC interface", - "subtype": "package", + type: "pointer", + name: "Bitcoin RPC Password", + description: "The password for Bitcoin Core's RPC interface", + subtype: "package", "package-id": "bitcoind", - "target": "config", - "multi": false, - "selector": "$.rpc.password" + target: "config", + multi: false, + selector: "$.rpc.password", }, - "lightning": { - "type": "union", - "tag": { - "id": "type", - "name": "Lightning Node", - "description": "Use this setting to grant access to the selected lightning node running on your server. If you prefer to use an external Lightning node, or you do not intend to use Lightning, leave this setting blank. Please see the \"Instructions\" page for more details.", + lightning: { + type: "union", + tag: { + id: "type", + name: "Lightning Node", + description: + 'Use this setting to grant access to the selected lightning node running on your server. If you prefer to use an external Lightning node, or you do not intend to use Lightning, leave this setting blank. Please see the "Instructions" page for more details.', "variant-names": { - "none": "No selection", + none: "None", "c-lightning": "Core Lightning", - "lnd": "LND" - } + lnd: "LND", + }, + }, + name: "Lightning Node", + description: + 'Use this setting to grant BTCPay access to your internal LND or Core Lightning node. If you prefer to use an external Lightning node, or you do not intend to use Lightning, leave this setting blank. Please see the "Instructions" page for more details.', + default: "none", + variants: { + none: {}, + lnd: {}, + "c-lightning": {}, + }, + }, + altcoins: { + type: "object", + name: "Altcoin Integrations", + description: + 'Choose which altcoins to enable. Please see the "Instructions" page for more details.', + spec: { + monero: { + type: "union", + name: "Monero", + description: "Enables integration with Monero for payment processing", + tag: { + id: "status", + name: "Monero", + "variant-names": { + enabled: "Enabled", + disabled: "Disabled", + }, + }, + default: "disabled", + variants: { + disabled: {}, + enabled: {}, + }, + }, }, - "name": "Lightning Node", - "description": "Use this setting to grant BTCPay access to your internal LND or Core Lightning node. If you prefer to use an external Lightning node, or you do not intend to use Lightning, leave this setting blank. Please see the \"Instructions\" page for more details.", - "default": "none", - "variants": { - "none": {}, - "lnd": {}, - "c-lightning": {} - } }, - "advanced": { - "type": "object", - "name": "Advanced Settings", - "description": "Advanced configuration options to change if you know what you are doing", - "spec": { + advanced: { + type: "object", + name: "Advanced Settings", + description: + "Advanced configuration options to change if you know what you are doing", + spec: { "sync-start-height": { - "type": "number", - "nullable": false, - "name": "Sync Start Height", - "description": "The block explorer within BTCPay will start scanning from the configured start height. This means that you might not see old payments from your HD key. If you need to see old payments, you need to configure the start height to a specific height of your choice. By default, this value is -1, which is used to indicate the current stored blockchain height.", - "range": "[-1,*)", - "integral": true, - "default": -1 - } - } - } -}) + type: "number", + nullable: false, + name: "Sync Start Height", + description: + "The block explorer within BTCPay will start scanning from the configured start height. This means that you might not see old payments from your HD key. If you need to see old payments, you need to configure the start height to a specific height of your choice. By default, this value is -1, which is used to indicate the current stored blockchain height.", + range: "[-1,*)", + integral: true, + default: -1, + }, + }, + }, +}); diff --git a/scripts/procedures/migrations.ts b/scripts/procedures/migrations.ts index 913965f..61425ab 100644 --- a/scripts/procedures/migrations.ts +++ b/scripts/procedures/migrations.ts @@ -2,6 +2,7 @@ import { types as T, compat } from "../deps.ts"; import { migration_up_1_4_7 } from "../migrations/1_4_7_up_migration.ts"; import { migration_down_1_4_7 } from "../migrations/1_4_7_down_migration.ts"; import { migration_up_1_10_3 } from "../migrations/1_10_3_up_migration.ts"; +import { migration_up_2_0_0 } from "../migrations/2_0_0_up_migration.ts"; export const migration: T.ExpectedExports.migration = async ( effects, @@ -40,19 +41,19 @@ export const migration: T.ExpectedExports.migration = async ( true, { version: "1.10.2", type: "up" } ), - down: compat.migrations.updateConfig( - (_config) => { - throw new Error("Cannot downgrade this version"); + down: () => { + throw new Error("Cannot downgrade this version"); + }, + }, + // major release incompatible downgrade, adds monero + "2.0.0": { + up: compat.migrations.updateConfig( + (config) => { + return migration_up_2_0_0(config); }, true, - { version: "1.10.2", type: "down" } + { version: "2.0.0", type: "up" } ), - }, - "2.0.0": { - up: compat.migrations.updateConfig((config) => config, true, { - version: "2.0.0", - type: "up", - }), down: () => { throw new Error("Cannot downgrade this version"); }, diff --git a/scripts/procedures/setConfig.ts b/scripts/procedures/setConfig.ts index 8a9827d..1a4cb84 100644 --- a/scripts/procedures/setConfig.ts +++ b/scripts/procedures/setConfig.ts @@ -1,16 +1,21 @@ -import { - compat, - types as T -} from "../deps.ts"; -export const setConfig: T.ExpectedExports.setConfig = async (effects, input ) => { +import { compat, types as T } from "../deps.ts"; +export const setConfig: T.ExpectedExports.setConfig = async ( + effects, + input +) => { // deno-lint-ignore no-explicit-any const newConfig = input as any; - const depsLnd: T.DependsOn = newConfig?.lightning?.type === "lnd" ? {lnd: []} : {} - const depsCln: T.DependsOn = newConfig?.lightning?.type === "c-lightning" ? {"c-lightning": []} : {} + const depsLnd: T.DependsOn = + newConfig?.lightning?.type === "lnd" ? { lnd: [] } : {}; + const depsCln: T.DependsOn = + newConfig?.lightning?.type === "c-lightning" ? { "c-lightning": [] } : {}; + const depsMonero: T.DependsOn = + newConfig?.altcoins.monero.status === "enabled" ? { monerod: [] } : {}; - return await compat.setConfig(effects,input, { + return await compat.setConfig(effects, input, { ...depsLnd, ...depsCln, - }) -} + ...depsMonero, + }); +}; diff --git a/utils/config/monero-wallet-rpc.btcpayserver.conf.template b/utils/config/monero-wallet-rpc.btcpayserver.conf.template new file mode 100644 index 0000000..5552dad --- /dev/null +++ b/utils/config/monero-wallet-rpc.btcpayserver.conf.template @@ -0,0 +1,27 @@ +# +# Monero wallet RPC config file for BTCPayServer's XMR wallet. +# Documentation: https://getmonero.dev/interacting/monero-wallet-rpc +# + +trusted-daemon=1 +daemon-host=monerod.embassy +daemon-port=18089 +# if empty, placeholder for if/when monerod rpc credentials are enabled +#daemon-login= +shared-ringdb-dir=MONERO_DIR + +wallet-dir=MONERO_BTCPAY_WALLET_DIR +#wallet-file=MONERO_BTCPAY_WALLET_DIR/wallet +#password-file=MONERO_BTCPAY_WALLET_DIR/password + +log-file=MONERO_DIR/logs/monero-wallet-rpc.btcpayserver.log +#log-level=2 +max-log-file-size=10000000 #10MiB +max-log-files=2 + +#confirm-external-bind=1 +rpc-bind-ip=127.0.0.1 +rpc-bind-port=MONERO_BTCPAY_WALLET_RPC_PORT +disable-rpc-login=1 + +tx-notify=/usr/bin/curl -so /dev/null -X GET http://127.0.0.1:23000/monerolikedaemoncallback/tx?cryptoCode=xmr&hash=%s diff --git a/utils/nginx.conf b/utils/config/nginx.conf similarity index 95% rename from utils/nginx.conf rename to utils/config/nginx.conf index ce530a6..bf35801 100644 --- a/utils/nginx.conf +++ b/utils/config/nginx.conf @@ -1,6 +1,7 @@ server { listen 80 default_server; listen [::]:80 default_server; + client_max_body_size 100M; location /notifications/subscribeupdates { diff --git a/utils/s6-overlay/contents.d/monero-wallet-rpc b/utils/s6-overlay/contents.d/monero-wallet-rpc new file mode 100644 index 0000000..e69de29 diff --git a/utils/s6-overlay/services/btcpayserver/dependencies b/utils/s6-overlay/services/btcpayserver/dependencies index 7f20ebc..c71f9ac 100644 --- a/utils/s6-overlay/services/btcpayserver/dependencies +++ b/utils/s6-overlay/services/btcpayserver/dependencies @@ -1 +1,2 @@ -nbxplorer \ No newline at end of file +nbxplorer +monero-wallet-rpc \ No newline at end of file diff --git a/utils/s6-overlay/services/btcpayserver/run b/utils/s6-overlay/services/btcpayserver/run index 4f31964..812a52f 100755 --- a/utils/s6-overlay/services/btcpayserver/run +++ b/utils/s6-overlay/services/btcpayserver/run @@ -39,8 +39,20 @@ then done fi +# check readiness of altcoin connections if configured to use +monero=$(yq e '.altcoins.monero.status' /datadir/start9/config.yaml) +if [[ $monero = "enabled" ]] +then + + if ! test -d /mnt/monerod + then + echo "Monero mountpoint does not exist" + exit 0 + fi + +fi -# configure templates and load environment variables generated for lightning connections +# configure templates and load environment variables generated for dependency connections configurator > .env source .env diff --git a/utils/s6-overlay/services/monero-wallet-rpc/dependencies b/utils/s6-overlay/services/monero-wallet-rpc/dependencies new file mode 100644 index 0000000..e69de29 diff --git a/utils/s6-overlay/services/monero-wallet-rpc/finish b/utils/s6-overlay/services/monero-wallet-rpc/finish new file mode 100644 index 0000000..e69de29 diff --git a/utils/s6-overlay/services/monero-wallet-rpc/run b/utils/s6-overlay/services/monero-wallet-rpc/run new file mode 100644 index 0000000..2f87408 --- /dev/null +++ b/utils/s6-overlay/services/monero-wallet-rpc/run @@ -0,0 +1,73 @@ +#!/command/with-contenv bash +# ^ allows access to the Docker container's environment variables + +# -a Mark variables which are modified or created for export +# -e Exit immediately if a command exits with a non-zero status +set -ea + +# Check readiness of altcoin connections if configured to use +monero_status=$(yq e '.altcoins.monero.status' /datadir/start9/config.yaml) +if [[ $monero_status = "enabled" ]] +then + CURRENT_DIR=$(pwd) + MONERO_DIR="/datadir/btcpayserver/altcoins/monero" + mkdir -p $MONERO_DIR + MONERO_UID=30236 + MONERO_GID=302340 + chown $MONERO_UID:$MONERO_GID $MONERO_DIR + chmod 755 $MONERO_DIR + CONF_FILENAME="monero-wallet-rpc.btcpayserver.conf" + MONERO_BTCPAY_WALLET_RPC_CONF="${MONERO_DIR}/${CONF_FILENAME}" + MONERO_BTCPAY_WALLET_RPC_PORT=18082 + # Change directory to where the monero wallets, conf, rpc db file & logs dir will go and auth file can be written successfully + cd "$MONERO_DIR" + # Remove old auth file in case it stuck around from an incomplete shutdown + mwr_auth_file="monero-wallet-rpc.${MONERO_BTCPAY_WALLET_RPC_PORT}.login" + if [ -f "$mwr_auth_file" ] ; then + rm -f "$mwr_auth_file" + fi + cd "$CURRENT_DIR" + + # Setup wallet logs + mkdir -p $MONERO_DIR/logs + wallet_log_file="$MONERO_DIR/logs/monero-wallet-rpc.btcpayserver.log" + if [ ! -f "$wallet_log_file" ] + then + touch $MONERO_DIR/logs/monero-wallet-rpc.btcpayserver.log + fi + + # Ensure that the monero wallet directory exists + MONERO_BTCPAY_WALLET_DIR=${MONERO_DIR}/wallets + mkdir -p $MONERO_BTCPAY_WALLET_DIR + chown $MONERO_UID:$MONERO_GID $MONERO_BTCPAY_WALLET_DIR + chmod 775 $MONERO_BTCPAY_WALLET_DIR + + # Construct a fully-formed monero-wallet-rpc config file from the template + cp -f /etc/$CONF_FILENAME.template $MONERO_BTCPAY_WALLET_RPC_CONF + chown $MONERO_UID:$MONERO_GID $MONERO_BTCPAY_WALLET_RPC_CONF + sed -i "s|MONERO_DIR|$MONERO_DIR|g" $MONERO_BTCPAY_WALLET_RPC_CONF && \ + sed -i "s|MONERO_BTCPAY_WALLET_RPC_PORT|$MONERO_BTCPAY_WALLET_RPC_PORT|g" $MONERO_BTCPAY_WALLET_RPC_CONF && \ + sed -i "s|MONERO_BTCPAY_WALLET_DIR|$MONERO_BTCPAY_WALLET_DIR|g" $MONERO_BTCPAY_WALLET_RPC_CONF && \ + + monero_daemon_rpc_credentials="" + monero_rpc_enabled="$(yq e '.rpc.rpc-credentials.enabled' /mnt/monerod/start9/config.yaml)" + # If a monero daemon RPC username has been setup: + if [ "$monero_rpc_enabled" == "enabled" ] + then + monero_daemon_rpc_username=$(yq e '.rpc.rpc-credentials.username' /mnt/monerod/start9/config.yaml) + monero_daemon_rpc_password=$(yq e '.rpc.rpc-credentials.password' /mnt/monerod/start9/config.yaml) + monero_daemon_rpc_credentials="$monero_daemon_rpc_username:$monero_daemon_rpc_password" + sed -i "s|#daemon-login=.*|daemon-login=$monero_daemon_rpc_credentials|g" $MONERO_BTCPAY_WALLET_RPC_CONF + fi + + if [[ -f "$MONERO_BTCPAY_WALLET_DIR/wallet" || -f "$MONERO_BTCPAY_WALLET_DIR/password" ]] + then + sed -i "s|wallet-dir=.*|#wallet-dir=$MONERO_BTCPAY_WALLET_DIR|g" $MONERO_BTCPAY_WALLET_RPC_CONF + sed -i "s|#wallet-file=.*|wallet-file=$MONERO_BTCPAY_WALLET_DIR/wallet|g" $MONERO_BTCPAY_WALLET_RPC_CONF + sed -i "s|#password-file=.*|password-file=$MONERO_BTCPAY_WALLET_DIR/password|g" $MONERO_BTCPAY_WALLET_RPC_CONF + fi + + # Start monero-wallet-rpc, copying stderr onto stdout + exec s6-setuidgid $MONERO_UID:$MONERO_GID /usr/local/bin/monero-wallet-rpc --non-interactive --config-file $MONERO_BTCPAY_WALLET_RPC_CONF 2>&1 + +fi diff --git a/utils/s6-overlay/services/monero-wallet-rpc/type b/utils/s6-overlay/services/monero-wallet-rpc/type new file mode 100644 index 0000000..1780f9f --- /dev/null +++ b/utils/s6-overlay/services/monero-wallet-rpc/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/utils/scripts/notifier.sh b/utils/scripts/notifier.sh new file mode 100644 index 0000000..989cd0d --- /dev/null +++ b/utils/scripts/notifier.sh @@ -0,0 +1 @@ +curl $@ \ No newline at end of file diff --git a/utils/scripts/postgres-init.sh b/utils/scripts/postgres-init.sh index 1b922a2..a16ca87 100755 --- a/utils/scripts/postgres-init.sh +++ b/utils/scripts/postgres-init.sh @@ -5,10 +5,9 @@ then echo "postgres already initialized" >&2 else echo "initializing postgres..." >&2 - chmod 777 /datadir mkdir -p /datadir/postgresql/data - chmod 777 /datadir/postgresql - chown -R postgres:postgres /datadir/postgresql/data + chown -R postgres:postgres /datadir/postgresql + chmod 770 /datadir/postgresql /datadir/postgresql/data exec s6-setuidgid postgres /usr/lib/postgresql/13/bin/initdb -D /datadir/postgresql/data 2>&1 echo "postgres initialization complete" >&2 fi \ No newline at end of file