diff --git a/Dockerfile b/Dockerfile index 333b744..dd23299 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM lightninglabs/lnd:v0.16.2-beta +FROM lightninglabs/lnd:v0.16.4-beta ARG ARCH ARG PLATFORM diff --git a/actions/import-mynode.sh b/actions/import-mynode.sh new file mode 100644 index 0000000..ce75eb6 --- /dev/null +++ b/actions/import-mynode.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +cat > input.json +MYNODE_HOST=$(jq -r '.["mynode-host"]' input.json) +MYNODE_PASS=$(jq -r '.["mynode-password"]' input.json) +rm input.json +>&2 echo "Stopping MyNode Services" +sshpass -p "$MYNODE_PASS" ssh -o StrictHostKeyChecking=no admin@$MYNODE_HOST "echo \"$MYNODE_PASS\" | >&2 sudo -S /usr/bin/mynode_stop_critical_services.sh" +>&2 echo "Copying LND data" +sshpass -p "$MYNODE_PASS" ssh -o StrictHostKeyChecking=no admin@$MYNODE_HOST "echo \"$MYNODE_PASS\" | >&2 sudo -S chmod -R 755 /mnt/hdd/mynode/lnd/data" +sshpass -p "$MYNODE_PASS" scp -o StrictHostKeyChecking=no -r -v admin@$MYNODE_HOST:"/mnt/hdd/mynode/lnd/data" /root/.lnd + +LN_CLI_PASS=$(sshpass -p "$MYNODE_PASS" ssh -o StrictHostKeyChecking=no admin@$MYNODE_HOST "echo \"$MYNODE_PASS\" | sudo -S cat /mnt/hdd/mynode/settings/.lndpw") +echo -n "$LN_CLI_PASS" > /root/.lnd/pwd.dat +echo '{"version":"0","message":"Successfully Imported MyNode Data","value":null,"copyable":false,"qr":false}' \ No newline at end of file diff --git a/actions/import-raspiblitz.sh b/actions/import-raspiblitz.sh new file mode 100644 index 0000000..72cf009 --- /dev/null +++ b/actions/import-raspiblitz.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +cat > input.json +RASPIBLITZ_HOST=$(jq -r '.["raspiblitz-host"]' input.json) +RASPIBLITZ_PASS=$(jq -r '.["raspiblitz-password"]' input.json) +RASPIBLITZ_LNCLI_PASS=$(jq -r '.["raspiblitz-lncli-password"]' input.json) +rm input.json +>&2 echo "Stopping RaspiBlitz LND" +sshpass -p "$RASPIBLITZ_PASS" ssh -o StrictHostKeyChecking=no admin@$RASPIBLITZ_HOST "lncli stop" +>&2 echo "Copying LND data" +sshpass -p "$RASPIBLITZ_PASS" ssh -o StrictHostKeyChecking=no admin@$RASPIBLITZ_HOST "echo \"$RASPIBLITZ_PASS\" | >&2 sudo -S chmod -R 755 /mnt/hdd/lnd/data" +sshpass -p "$RASPIBLITZ_PASS" scp -o StrictHostKeyChecking=no -r -v admin@$RASPIBLITZ_HOST:"/mnt/hdd/lnd/data" /root/.lnd + +echo -n "$RASPIBLITZ_LNCLI_PASS" > /root/.lnd/pwd.dat +echo '{"version":"0","message":"Successfully Imported RaspiBlitz Data","value":null,"copyable":false,"qr":false}' \ No newline at end of file diff --git a/actions/import-umbrel-5.sh b/actions/import-umbrel-5.sh index 2b5d63d..4dc1815 100644 --- a/actions/import-umbrel-5.sh +++ b/actions/import-umbrel-5.sh @@ -7,8 +7,8 @@ UMBREL_HOST=$(jq -r '.["umbrel-host"]' input.json) UMBREL_PASS=$(jq -r '.["umbrel-password"]' input.json) rm input.json >&2 echo "Stopping Umbrel Services" -sshpass -p $UMBREL_PASS ssh -o StrictHostKeyChecking=no umbrel@$UMBREL_HOST "echo $UMBREL_PASS | >&2 sudo -S /home/umbrel/umbrel/scripts/stop" +sshpass -p "$UMBREL_PASS" ssh -o StrictHostKeyChecking=no umbrel@$UMBREL_HOST "echo \"$UMBREL_PASS\" | >&2 sudo -S /home/umbrel/umbrel/scripts/stop" >&2 echo "Copying LND Data" -sshpass -p $UMBREL_PASS scp -o StrictHostKeyChecking=no -r -v umbrel@$UMBREL_HOST:/home/umbrel/umbrel/app-data/lightning/data/lnd/* /root/.lnd +sshpass -p "$UMBREL_PASS" scp -o StrictHostKeyChecking=no -r -v umbrel@$UMBREL_HOST:/home/umbrel/umbrel/app-data/lightning/data/lnd/* /root/.lnd echo -n 'moneyprintergobrrr' > /root/.lnd/pwd.dat echo '{"version":"0","message":"Successfully Imported Umbrel Data","value":null,"copyable":false,"qr":false}' diff --git a/actions/import-umbrel.sh b/actions/import-umbrel.sh index 2470347..967804d 100755 --- a/actions/import-umbrel.sh +++ b/actions/import-umbrel.sh @@ -7,8 +7,8 @@ UMBREL_HOST=$(jq -r '.["umbrel-host"]' input.json) UMBREL_PASS=$(jq -r '.["umbrel-password"]' input.json) rm input.json >&2 echo "Stopping Umbrel Services" -sshpass -p $UMBREL_PASS ssh -o StrictHostKeyChecking=no umbrel@$UMBREL_HOST "echo $UMBREL_PASS | >&2 sudo -S /home/umbrel/umbrel/scripts/stop" +sshpass -p "$UMBREL_PASS" ssh -o StrictHostKeyChecking=no umbrel@$UMBREL_HOST "echo \"$UMBREL_PASS\" | >&2 sudo -S /home/umbrel/umbrel/scripts/stop" >&2 echo "Copying LND Data" -sshpass -p $UMBREL_PASS scp -o StrictHostKeyChecking=no -r -v umbrel@$UMBREL_HOST:/home/umbrel/umbrel/lnd/* /root/.lnd +sshpass -p "$UMBREL_PASS" scp -o StrictHostKeyChecking=no -r -v umbrel@$UMBREL_HOST:/home/umbrel/umbrel/lnd/* /root/.lnd echo -n 'moneyprintergobrrr' > /root/.lnd/pwd.dat echo '{"version":"0","message":"Successfully Imported Umbrel Data","value":null,"copyable":false,"qr":false}' diff --git a/configurator/src/main.rs b/configurator/src/main.rs index 6dab09c..1cbd8e6 100644 --- a/configurator/src/main.rs +++ b/configurator/src/main.rs @@ -7,7 +7,7 @@ use std::path::Path; use std::process::Command; use std::str::FromStr; use std::{ - io::{Read, Write}, + io::{self, Read, Write}, time::Duration, }; @@ -119,8 +119,6 @@ enum BitcoinCoreConfig { None, #[serde(rename_all = "kebab-case")] Internal { user: String, password: String }, - #[serde(rename_all = "kebab-case")] - InternalProxy { user: String, password: String }, } #[derive(Deserialize)] @@ -207,7 +205,7 @@ pub struct Property { masked: bool, } -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, serde::Serialize)] pub struct CipherSeedMnemonic { cipher_seed_mnemonic: Vec, } @@ -261,6 +259,14 @@ pub fn local_port_available(port: u16) -> Result { } } } + +fn save_to_file(cipher_seed_mnemonic: &[String], file_path: &str) -> io::Result<()> { + let mut file = File::create(file_path)?; + for (i, word) in cipher_seed_mnemonic.iter().enumerate() { + writeln!(file, "{} {}", i + 1, word)?; + } + Ok(()) +} struct WatchtowerUri { pubkey: String, @@ -326,15 +332,6 @@ fn main() -> Result<(), anyhow::Error> { 28332, 28333, ), - BitcoinCoreConfig::InternalProxy { user, password } => ( - user, - password, - "btc-rpc-proxy.embassy", - 8332, - "bitcoind.embassy", - 28332, - 28333, - ), }; let rpc_info = &BitcoindRpcInfo { @@ -555,7 +552,7 @@ fn main() -> Result<(), anyhow::Error> { None => (), Some(backups) => { while local_port_available(8080)? { - std::thread::sleep(Duration::from_secs(10)) + std::thread::sleep(Duration::from_secs(20)) } let mac = std::fs::read(Path::new( "/root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon", @@ -626,50 +623,71 @@ fn main() -> Result<(), anyhow::Error> { }, } } else { - println!("creating password data"); + + let mut cipher_seed_created = false; let mut password_bytes = [0; 16]; let mut dev_random = File::open("/dev/random")?; - dev_random.read_exact(&mut password_bytes)?; - let output = std::process::Command::new("curl") - .arg("--no-progress-meter") - .arg("-X") - .arg("GET") - .arg("--cacert") - .arg("/root/.lnd/tls.cert") - .arg("https://lnd.embassy:8080/v1/genseed") - .arg("-d") - .arg(format!("{}", serde_json::json!({}))) - .output()?; - if !output.status.success() { - eprintln!("{}", std::str::from_utf8(&output.stderr)?); - return Err(anyhow::anyhow!("Error generating seed. Exiting.")); - } - let CipherSeedMnemonic { - cipher_seed_mnemonic, - } = serde_json::from_slice(&output.stdout)?; - let status = std::process::Command::new("curl") - .arg("--no-progress-meter") - .arg("-X") - .arg("POST") - .arg("--cacert") - .arg("/root/.lnd/tls.cert") - .arg("https://lnd.embassy:8080/v1/initwallet") - .arg("-d") - .arg(format!( - "{}", - serde_json::json!({ - "wallet_password": base64::encode(&password_bytes), - "cipher_seed_mnemonic": cipher_seed_mnemonic, - }) - )) - .status()?; - if status.success() { - let mut pass_file = File::create("/root/.lnd/pwd.dat")?; - pass_file.write_all(&password_bytes)?; - } else { - return Err(anyhow::anyhow!("Error creating wallet. Exiting.")); + let file_path = "/root/.lnd/start9/cipherSeedMnemonic.txt"; + + while !cipher_seed_created { + println!("creating password data"); + dev_random.read_exact(&mut password_bytes)?; + let output = std::process::Command::new("curl") + .arg("--no-progress-meter") + .arg("-X") + .arg("GET") + .arg("--cacert") + .arg("/root/.lnd/tls.cert") + .arg("https://lnd.embassy:8080/v1/genseed") + .arg("-d") + .arg(format!("{}", serde_json::json!({}))) + .output()?; + if !output.status.success() { + eprintln!("{}", std::str::from_utf8(&output.stderr)?); + return Err(anyhow::anyhow!("Error generating seed. Exiting.")); + } + + if let Ok(CipherSeedMnemonic { + cipher_seed_mnemonic, + }) = serde_json::from_slice(&output.stdout) { + println!("CipherSeed successfully generated"); + + if let Err(err) = save_to_file(&cipher_seed_mnemonic, file_path) { + eprintln!("Failed to save the CipherSeedMnemonic: {}", err); + } else { + println!("CipherSeedMnemonic saved to '{}'", file_path); + } + + let status = std::process::Command::new("curl") + .arg("--no-progress-meter") + .arg("-X") + .arg("POST") + .arg("--cacert") + .arg("/root/.lnd/tls.cert") + .arg("https://lnd.embassy:8080/v1/initwallet") + .arg("-d") + .arg(format!( + "{}", + serde_json::json!({ + "wallet_password": base64::encode(&password_bytes), + "cipher_seed_mnemonic": cipher_seed_mnemonic, + }) + )) + .status()?; + if status.success() { + let mut pass_file = File::create("/root/.lnd/pwd.dat")?; + pass_file.write_all(&password_bytes)?; + } else { + return Err(anyhow::anyhow!("Error creating wallet. Exiting.")); + } + cipher_seed_created = true + } else { + println!("Waiting for RPC to start..."); + std::thread::sleep(Duration::from_secs(5)); + } } } + println!("copying macaroon to public dir..."); while !Path::new("/root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon").exists() { std::thread::sleep(std::time::Duration::from_secs(1)); diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh index 83a6eac..254933b 100755 --- a/docker_entrypoint.sh +++ b/docker_entrypoint.sh @@ -48,7 +48,7 @@ lnd_child=$! while ! [ -e /root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon ]; do echo "Waiting for lnd to create macaroon..." - sleep 1 + sleep 30 done cat /root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon | basenc --base16 -w0 > /root/.lnd/start9/admin.macaroon.hex diff --git a/docs/instructions.md b/docs/instructions.md index 42b81f3..bee3634 100644 --- a/docs/instructions.md +++ b/docs/instructions.md @@ -10,9 +10,9 @@ Your LND node is highly configurable. Many settings are considered _advanced_ an ### Dependencies -On Embassy, you have 3 options with regard to Bitcoin +On StartOS, you have 3 options with regard to Bitcoin 1. Run an archival node. This is the default (recommended) -1. Run a pruned node. This will require installing Bitcoin Proxy from the Marketplace +1. Run a pruned node. 1. Do not run a Bitcoin node. LND will use Neutrino instead (not recommended) ## Using Your Node @@ -23,22 +23,21 @@ For a list of compatible wallets and related instructions, see = 16.3 the Aezeed Cipher Seed is exposed in the `Properties` of LND. *WARNING* The seed in properties has no knowledge of channel state, as such it can only be used to recover on-chain funds. Despite the Aezeed Cipher Seed appearing similar to a BIP39 seed, the Azeez Cipher Seed is *NOT* the same and cannot be used to recover on-chain funds to any wallet other than LND. -Be advised, if you evern need to recover from backup, _your channels will be closed_ and all channel funds will be moved to your on-chain balance. This is a necessary aspect of the way LND works and backups are created. +Be advised, if you ever need to recover from backup, _your channels will be closed_ and all channel funds will be moved to your on-chain balance. This is a necessary aspect of the way LND works and backups are created. ## Watchtowers @@ -56,12 +55,12 @@ In LND, watchtowers act as a second line of defense in responding to malicious o ### Being a Watchtower for Others -You can make your LND node a watchtower for others by enabling in settings. There is no immediate economic reason to do this, but you may want to do it for friends or family, or a second LND node of your own. Once enabled, you share your watchtower public key with whomever you want to use it. +You can make your LND node a watchtower for others by clicking the toggle `Enable Watchtower Server` in settings. There is no immediate economic reason to do this, but you may want to do it for friends or family, or a second LND node of your own. Once enabled, you share your watchtower public key with whomever you want to use it. Be advised, your watchtower’s public key is *different* from lnd’s node public key. It is not known the network. We recommend NOT disclosing this public key openly, unless you are prepared to open your tower up to the entire Internet. To obtain your full LND Watchtower URI: -1. SSH into your Embassy +1. SSH into your server 1. Run `sudo docker exec -ti lnd.embassy lncli --rpcserver=lnd.embassy tower info` 1. Copy the entry under `uris` and give it to anyone for whom you would like to be a watchtower @@ -70,7 +69,7 @@ To obtain your full LND Watchtower URI: You can enlist watchtowers to watch your node by using `Add a watchtower to your LND Node` in Actions or in Config options. This will back up your LND node state to the remote watchtower you entered. After adding a watchtower URI through Actions or Config, you can confirm it is working by: -1. SSH into your Embassy +1. SSH into your server 1. Run `sudo docker exec -ti lnd.embassy lncli --rpcserver=lnd.embassy wtclient towers` 1. If you see `"active_session_candidate": true`, it worked. If not, double check the watchtower URI you were provided and try again. diff --git a/manifest.yaml b/manifest.yaml index 0a1b3f1..c454e8f 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,8 +1,12 @@ id: lnd title: LND -version: 0.16.2.2 +version: 0.16.4 release-notes: |- - * Bugfix with cert generation on first run + * Update upstream to lnd [v0.16.4-beta](https://github.com/lightningnetwork/lnd/releases/tag/v0.16.4-beta) + * Add actions to migrate from myNode and RaspiBlitz + * Remove Proxy as a dependency + * Expose Aezeed CipherSeed in properties for wallets created on >= 16.4 + * Fix non-deterministic bug where a CipherSeed is not created on a fresh install license: mit wrapper-repo: "https://github.com/Start9Labs/lnd-wrapper" upstream-repo: "https://github.com/lightningnetwork/lnd" @@ -97,22 +101,11 @@ interfaces: - tcp - http dependencies: - btc-rpc-proxy: - version: ">=0.3.2.6 <0.4.0" - requirement: - type: "opt-in" - how: Can alternatively use the internal full archival bitcoind node or configure an external bitcoin node. - description: Used to fetch validated blocks. - config: - check: - type: script - auto-configure: - type: script bitcoind: - version: ">=0.21.1.2 <26.0.0" + version: ">=0.21.1.2 <27.0.0" requirement: type: "opt-out" - how: Can alternatively configure an external bitcoin node. + how: "Can alternatively use Neutrino" description: Used to subscribe to new block events. config: check: @@ -197,6 +190,76 @@ actions: placeholder: password nullable: false default: "" + import-mynode: + name: "Import from MyNode" + description: "Imports wallet and channel data from MyNode" + warning: "This will destroy existing channel data, please make sure you have no funds on this node before running this action" + allowed-statuses: + - stopped + implementation: + type: docker + image: main + system: false + entrypoint: import-mynode.sh + args: [] + io-format: json + mounts: + main: /root/.lnd + input-spec: + mynode-host: + type: string + name: MyNode IP Address + description: "The IP Address for your MyNode. You can find this by running the command `ping mynode.local` while connected to your LAN." + masked: false + placeholder: "192.168.1.7" + nullable: false + mynode-password: + type: string + name: MyNode Password + description: "The password you use to log into your MyNode dashboard or SSH" + masked: true + placeholder: bolt + nullable: false + default: "" + import-raspiblitz: + name: "Import from RaspiBlitz" + description: "Imports wallet and channel data from RaspiBlitz" + warning: "This will destroy existing channel data, please make sure you have no funds on this node before running this action" + allowed-statuses: + - stopped + implementation: + type: docker + image: main + system: false + entrypoint: import-raspiblitz.sh + args: [] + io-format: json + mounts: + main: /root/.lnd + input-spec: + raspiblitz-host: + type: string + name: RaspiBlitz IP Address + description: "The IP Address for your RaspiBlitz. To find this IP address, you may need to check the DHCP Leases in your router." + masked: false + placeholder: "192.168.1.7" + nullable: false + raspiblitz-password: + type: string + name: RaspiBlitz SSH Password + description: "The password you use to log into your RaspiBlitz via SSH (Password A)" + masked: true + placeholder: raspiblitz + nullable: false + default: "" + raspiblitz-lncli-password: + type: string + name: RaspiBlitz lncli Password + description: "The password used to unlock LND on RaspiBlitz (Password C)" + masked: true + placeholder: raspiblitz_lncli + nullable: false + default: "" import-umbrel: name: "Import from Umbrel (0.4)" description: "Imports wallet and channel data from Umbrel 0.4" diff --git a/scripts/services/dependencies.ts b/scripts/services/dependencies.ts index 560c9a1..faa1143 100644 --- a/scripts/services/dependencies.ts +++ b/scripts/services/dependencies.ts @@ -1,177 +1,47 @@ import { matches, types as T } from "../deps.ts"; -const { shape, arrayOf, string, boolean } = matches; +const { shape, boolean, string } = 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 = "lnd"; -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(""), - }); - }, - }, - ...[ - "getinfo", - "getbestblockhash", - "gettxout", - "getblockchaininfo", - "sendrawtransaction", - "getblockhash", - "getblock", - "getblockheader", - "estimatesmartfee", - "getnetworkinfo", - "uptime", - "getrawtransaction", - "getpeerinfo", - "getmempoolinfo", - "getzmqnotifications", - "getdeploymentinfo", - "getrawmempool", - ].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]; - }, - }), - ), - { - currentError(config) { - if (!matchProxyConfig.test(config)) { - return "Config is not the correct shape"; - } - if ( - config.users.find((x) => x.name === serviceName)?.["fetch-blocks"] ?? - false - ) { - return; - } - return `RPC user "${serviceName}" must have "Fetch Blocks" 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["fetch-blocks"] = true; - }, - }, -]; - -const matchBitcoindConfig = shape({ +const matchOldBitcoindConfig = shape({ "zmq-enabled": boolean, -}); + rpc: shape({ + advanced: shape({ + serialversion: matches.any + }), + }), + advanced: shape({ + pruning: shape({ + mode: string, + }), + }), +}) 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["zmq-enabled"]) { + if (matchOldBitcoindConfig.test(configInput) && configInput.advanced.pruning.mode !== "disabled") { + return { error: 'Pruning must be disabled to use CLN with <= 24.0.1 of Bitcoin Core. To use CLN with a pruned node, update Bitcoin Core to >= 25.0.0~2.' }; + } else if (matchOldBitcoindConfig.test(configInput) && !configInput['zmq-enabled']) { + return { error: "Must have ZeroMQ enabled" }; + } else if (matchOldBitcoindConfig.test(configInput)) { + return { result: null } + } + if (!configInput["zmq-enabled"]) { return { error: "Must have ZeroMQ enabled" }; } return { result: null }; }, // deno-lint-ignore require-await async autoConfigure(_effects, configInput) { - const config = matchBitcoindConfig.unsafeCast(configInput); - config["zmq-enabled"] = true; - return { result: config }; + if (matchOldBitcoindConfig.test(configInput)) { + configInput.advanced.pruning.mode = "disabled" + configInput["zmq-enabled"] = true; + return { result: configInput } + } else { + configInput["zmq-enabled"] = true; + return { result: configInput }; + } }, }, }; diff --git a/scripts/services/getConfig.ts b/scripts/services/getConfig.ts index ca4f50b..3a32f77 100644 --- a/scripts/services/getConfig.ts +++ b/scripts/services/getConfig.ts @@ -117,17 +117,16 @@ export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ "type": "union", "name": "Bitcoin Core", "description": - "

The Bitcoin Core node to connect to:

  • None: Use the light bitcoin backend built into LND, Neutrino. If using Neutrino, please switch to using Bitcoin Core as soon as possible. Neutrino uses the BIP157/8 light client protocol, which has security risks.

  • Bitcoin Core/Proxy: either service installed on your Embassy. Neutrino will also be used during IBD.
", + "

The Bitcoin Core node to connect to:

  • None: Use the light bitcoin backend built into LND, Neutrino. If using Neutrino, please switch to using Bitcoin Core as soon as possible. Neutrino uses the BIP157/8 light client protocol, which has security risks.

  • Bitcoin Core: service installed on your server. Neutrino will also be used during IBD.
", "tag": { "id": "type", "name": "Type", "variant-names": { "none": "None (Built-in LND Neutrino)", "internal": "Bitcoin Core", - "internal-proxy": "Bitcoin Proxy", }, "description": - "

The Bitcoin Core node to connect to:

  • None: Use the light bitcoin backend built into LND, Neutrino. If using Neutrino, please switch to using Bitcoin Core as soon as possible. Neutrino uses the BIP157/8 light client protocol, which has security risks.

  • Bitcoin Core/Proxy: either service installed on your Embassy. Neutrino will also be used during IBD.
", + "

The Bitcoin Core node to connect to:

  • None: Use the light bitcoin backend built into LND, Neutrino. If using Neutrino, please switch to using Bitcoin Core as soon as possible. Neutrino uses the BIP157/8 light client protocol, which has security risks.

  • Bitcoin Core: service installed on your server. Neutrino will also be used during IBD.
", }, "warning": "If using Neutrino, please switch to using Bitcoin Core as soon as possible. Neutrino uses the BIP157/8 light client protocol, which has security risks.", @@ -156,28 +155,6 @@ export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ "selector": "$.rpc.password", }, }, - "internal-proxy": { - "user": { - "type": "pointer", - "name": "RPC Username", - "description": "The username for the RPC user allocated to lnd", - "subtype": "package", - "package-id": "btc-rpc-proxy", - "target": "config", - "multi": false, - "selector": '$.users[?(@.name == "lnd")].name', - }, - "password": { - "type": "pointer", - "name": "RPC Password", - "description": "The password for the RPC user allocated to lnd", - "subtype": "package", - "package-id": "btc-rpc-proxy", - "target": "config", - "multi": false, - "selector": '$.users[?(@.name == "lnd")].password', - }, - }, }, }, "autopilot": { diff --git a/scripts/services/migrations.ts b/scripts/services/migrations.ts index 3242240..36841c4 100644 --- a/scripts/services/migrations.ts +++ b/scripts/services/migrations.ts @@ -133,6 +133,17 @@ export const migration: T.ExpectedExports.migration = compat.migrations ), down: () => { throw new Error('Cannot downgrade') }, }, + "0.16.4": { + up: compat.migrations.updateConfig( + (config: any) => { + if (config.bitcoind.type === 'internal-proxy') config.bitcoind.type = 'internal' + return config; + }, + true, + { version: "0.16.4", type: "up" }, + ), + down: () => { throw new Error('Cannot downgrade') }, + } }, - "0.16.2.2", + "0.16.4", ); diff --git a/scripts/services/properties.ts b/scripts/services/properties.ts index 757e811..e440032 100644 --- a/scripts/services/properties.ts +++ b/scripts/services/properties.ts @@ -45,7 +45,7 @@ const wrongShape = (wrongValue: unknown): T.ResultType => export const properties: T.ExpectedExports.properties = async ( effects: T.Effects ) => { - const paths = ["start9/controlTorAddress", "start9/peerTorAddress"]; + const paths = ["start9/controlTorAddress", "start9/peerTorAddress", "start9/admin.macaroon.hex", "start9/admin.macaroon.base64url", "start9/control.cert.pem.base64url"]; const exists = async (path: string): Promise => await util.exists(effects, { volumeId: "main", path }); if (!(await Promise.all(paths.map(exists))).every((v) => v)) @@ -57,19 +57,15 @@ export const properties: T.ExpectedExports.properties = async ( macaroonHex, macaroonBase64URL, cert, + cipherSeedMnemonic, ] = await Promise.all([ ...paths.map(async (path) => (await effects.readFile({ volumeId: "main", path })).trim() ), - effects.readFile({ volumeId: "main", path: "start9/admin.macaroon.hex" }), effects.readFile({ volumeId: "main", - path: "start9/admin.macaroon.base64url", - }), - effects.readFile({ - volumeId: "main", - path: "start9/control.cert.pem.base64url", - }), + path: "start9/cipherSeedMnemonic.txt", + }).catch(() => "no cipherSeed found"), ]); try { @@ -154,6 +150,14 @@ export const properties: T.ExpectedExports.properties = async ( qr: true, masked: true, }, + "LND Aezeed Cypherseed": { + type: "string", + value: `${cipherSeedMnemonic !== "no cipherSeed found"? cipherSeedMnemonic : "The Aezeed Cipher Seed is only available on StartOS for LND wallets created with >= 16.4. It is not possible to retreive the Seed from wallets created on < 16.4.\nIf you are using a LND wallet created pre 16.4 but would like to have a Cipher Seed backup, you will need to close your existing channels and move any on-chain funds to an intermediate wallet before creating a new LND wallet with >= 16.4."}`, + description: "Seed for restoring on-chain ONLY funds. This seed has no knowledge of channel state. This is NOT a BIP-39 seed; As such it cannot be used to recover on-chain funds to any wallet other than LND.", + copyable: true, + qr: false, + masked: true, + }, }, }; // Include the original stats object here diff --git a/scripts/services/setConfig.ts b/scripts/services/setConfig.ts index 9f58f2b..281e2e8 100644 --- a/scripts/services/setConfig.ts +++ b/scripts/services/setConfig.ts @@ -41,8 +41,7 @@ export const setConfig: T.ExpectedExports.setConfig = async ( const error = checkConfigRules(config); if (error) return error; const dependsOn: { [key: string]: string[] } = - config.bitcoind.type === "internal" || - config.bitcoind.type === "internal-proxy" + config.bitcoind.type === "internal" ? { "bitcoind": [] } : {}; return await compat.setConfig(effects, input, dependsOn);