mirror of
https://github.com/aljazceru/cyphernode.git
synced 2025-12-26 17:15:08 +01:00
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.idea
|
||||
dist/**
|
||||
!dist/setup.sh
|
||||
!dist/sr.sh
|
||||
79
README.md
79
README.md
@@ -1,47 +1,78 @@
|
||||
# cyphernode
|
||||
|
||||
Modular Bitcoin full-node microservices API server architecture and utilities toolkit to build scalable, secure and featureful apps and services without trusted third parties.
|
||||
Modular Bitcoin full-node microservices API server architecture and utilities toolkit to build scalable, secure and featureful apps and services without trusted third parties.
|
||||
|
||||
# What is cyphernode?
|
||||
|
||||
An open-source self-hosted API which allows you to spawn and call your encrypted overlay network of dockerized Bitcoin and crypto software projects (virtual machines).
|
||||
Cyphernode is a Free open-source alternative to hosted services and commercial Bitcoin APIs such as Blockchain.info, Bitpay, Coinbase, BlockCypher, Bitgo, etc. You can use it to build Bitcoin services and applications using your own Bitcoin and Lightning Network full nodes. It is a substitute for the Bitcore and Insight software projects.
|
||||
|
||||
You can use it to build Bitcoin services and applications using your own Bitcoin and Lightning Network full nodes.
|
||||
It implements a self-hosted API which allows you to spawn and call your encrypted overlay network of dockerized Bitcoin and crypto software projects (virtual machines). The Docker containers used in this project are hosted at www.bitcoindockers.com.
|
||||
|
||||
It is an alternative to hosted services and commercial Bitcoin APIs such as Blockchain.info, Bitpay, Coinbase, Blocypher, Bitgo, etc.
|
||||
|
||||
It is a substitute for the Bitcore and Insight software projects.
|
||||
|
||||
If aims to offer all advanced features and utilities necessary to operate entreprise grade Bitcoin services. We provide a curated list of functions using multiple software, but you can build your own private ones or add yours as a default option in the project.
|
||||
|
||||
It is designed to be deployed on virtual machines with launch scripts, but with efficiency and minimalism in mind so that it can also run on multiple Rasberry Pi with very low computing ressources (and extremely low if installing pre-synchronized blockchain and pruned). Because of the modular architecture, heavier modules like blockchain indexers are optional (and not needed for most commercial use-cases). For a full-node and all modules, prepare 350GB of space and 2GB of RAM.
|
||||
|
||||
Hardware wallets (ColdCard, Trezor) will be utilized for key generation and signing (using PSBT BIP174), as well as for connecting to self-hosted web user interfaces.
|
||||
It aims to offer all advanced features and utilities necessary to operate entreprise-grade Bitcoin services. It includes a curated list of functions using multiple software, but you can build your own private ones or add yours as a default option in the project.
|
||||
|
||||
It is currently in production by Bylls.com, Canada's first and largest Bitcoin payment processor, as well as Bitcoin Outlet, a fixed-rate Bitcoin exchange service alternative to Coinbase which allows Canadians to purchase bitcoins sent directly to their own Bitcoin wallet.
|
||||
|
||||
The docker containers used in this project are hosted at www.bitcoindockers.com
|
||||
The project is in **heavy development** - we are currently looking for reviews, new features, user feedback and contributors to our roadmap.
|
||||
|
||||
The project is in **heavy development** - we are currently looking for review, new features, user feedback and contributors to our roadmap.
|
||||

|
||||
|
||||
# Low requirements, efficient use of resources
|
||||
|
||||
Cyphernode is designed to be deployed on virtual machines with launch scripts, but with efficiency and minimalism in mind so that it can also run on multiple Rasberry Pi with very low computing ressources (and extremely low if installing pre-synchronized blockchain and pruned). Because of the modular architecture, heavier modules like blockchain indexers are optional (and not needed for most commercial use-cases).
|
||||
|
||||
* For a full-node and all modules:
|
||||
* 350 GB of storage, 2GB of RAM.
|
||||
* Hardware wallets (ColdCard, Trezor) for key generation and signing (using PSBT BIP174), as well as for connecting to self-hosted web user interfaces.
|
||||
|
||||
# Cyphernode Architecture
|
||||
Cyphernode is an assembly of Docker containers being called by a request dispatcher.
|
||||
|
||||
The request dispatcher (requesthandler.sh) is the HTTP entry point.
|
||||
The request dispatcher is stateful: it keeps some data to be more effective on next calls.
|
||||
The request dispatcher is where Cyphernode scales with new features: add your switch, dispatch requests to your stuff.
|
||||
We are trying to construct each container so that it can be used separately, as a standalone reusable component.
|
||||
|
||||
Important to us:
|
||||
|
||||
Be as optimized as possible, using Alpine when possible and having the smallest Docker image size possible
|
||||
Reuse existing software: built-in shell commands, well-established pieces of software, etc.
|
||||
Use open-source software
|
||||
Don't reinvent the wheel
|
||||
Expose the less possible surface
|
||||
Center element: proxy_docker
|
||||
The proxy_docker is the container receiving and dispatching calls from clients. When adding a feature to Cyphernode, it is the first part to be modified to integrate the new feature.
|
||||
|
||||
proxy_docker/app/script/requesthandler.sh
|
||||
You will find in there the switch statement used to dispatch the requests. Just add a case block with your command, using other cases as examples for POST or GET requests.
|
||||
|
||||
proxy_docker/app/config
|
||||
You will find there config files. config.properties should be used to centralize configs. spender and watcher properties are used to obfuscate credentials on curl calls.
|
||||
|
||||
proxy_docker/app/data
|
||||
watching.sql contains the data model. Called "watching" because in the beginning of the project, it was only used for watching addresses. Now could be used to index the blockchain (build an explorer) and add more features.
|
||||
|
||||
cron_docker
|
||||
If you have jobs to be scheduled, use this container. Just add an executable and add it to the crontab.
|
||||
|
||||
Currently used to make sure callbacks have been called for missed transactions.
|
||||
|
||||
# About this project
|
||||
|
||||
- Created and maintained by www.satoshiportal.com
|
||||
- Dedicated full-time developer @kexkey
|
||||
- Project manager @FrancisPouliot
|
||||
- Disclaimer: as of release on Sept. 23 2018 the project is still it its early stages (Alpha) and many of the features have yet to be implemented. The core architecture and basic wallet operations and blockchain query functions are fully functional.
|
||||
* Created and maintained by www.satoshiportal.com
|
||||
* Dedicated full-time developer @kexkey
|
||||
* Project manager @FrancisPouliot
|
||||
* Contributor: @\_\_escapee\_\_
|
||||
* Disclaimer: as of release on Sept. 23 2018 the project is still it its early stages (Alpha) and many of the features have yet to be implemented. The core architecture and basic wallet operations and blockchain query functions are fully functional.
|
||||
|
||||
# How to use cyphernode?
|
||||
|
||||
The core component of cyphernode is a request handler which exposes HTTP endpoints via REST API, acting as an absctration layer between your apps and the open-source Bitcoin sofware you want to interact with.
|
||||
|
||||
## DOCS
|
||||
## Documentation
|
||||
|
||||
Read the API docs here: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/API.md
|
||||
|
||||
Installation documentation: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL.md
|
||||
|
||||
Step-by-step manual install: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL-MANUAL-STEPS.md
|
||||
* Read the API docs here: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/API.md
|
||||
* Installation documentation: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL.md
|
||||
* Step-by-step manual install (deprecated): https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL-MANUAL-STEPS.md
|
||||
|
||||
## When calling a cyphernode endpoint, you are either
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ RUN apk add --update --no-cache \
|
||||
su-exec
|
||||
|
||||
COPY auth.sh /etc/nginx/conf.d/
|
||||
COPY default-ssl.conf /etc/nginx/conf.d/default.conf
|
||||
COPY statuspage.html /etc/nginx/conf.d/status/
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY entrypoint.sh entrypoint.sh
|
||||
COPY trace.sh /etc/nginx/conf.d/
|
||||
COPY tests.sh /etc/nginx/conf.d/
|
||||
|
||||
RUN chmod +x /etc/nginx/conf.d/auth.sh entrypoint.sh
|
||||
|
||||
RUN touch /var/log/gatekeeper.log
|
||||
RUN chmod a+rw /var/log/gatekeeper.log
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
# The file api.properties generated by the installer should look like this.
|
||||
|
||||
# Watcher can:
|
||||
# Stats can:
|
||||
action_helloworld=stats
|
||||
action_getblockchaininfo=stats
|
||||
action_installation_info=stats
|
||||
action_getmempoolinfo=stats
|
||||
action_getblockhash=stats
|
||||
|
||||
# Watcher can do what the stats can do, plus:
|
||||
action_watch=watcher
|
||||
action_unwatch=watcher
|
||||
action_watchxpub=watcher
|
||||
action_unwatchxpubbyxpub=watcher
|
||||
action_unwatchxpubbylabel=watcher
|
||||
action_getactivewatchesbyxpub=watcher
|
||||
action_getactivewatchesbylabel=watcher
|
||||
action_getactivexpubwatches=watcher
|
||||
action_watchtxid=watcher
|
||||
action_getactivewatches=watcher
|
||||
action_getbestblockhash=watcher
|
||||
action_getbestblockinfo=watcher
|
||||
@@ -10,11 +24,16 @@ action_getblockinfo=watcher
|
||||
action_gettransaction=watcher
|
||||
action_ln_getinfo=watcher
|
||||
action_ln_create_invoice=watcher
|
||||
action_ln_getconnectionstring=watcher
|
||||
action_ln_decodebolt11=watcher
|
||||
|
||||
# Spender can do what the watcher can do, plus:
|
||||
action_getbalance=spender
|
||||
action_getbalancebyxpub=spender
|
||||
action_getbalancebyxpublabel=spender
|
||||
action_getnewaddress=spender
|
||||
action_spend=spender
|
||||
action_bumpfee=spender
|
||||
action_addtobatch=spender
|
||||
action_batchspend=spender
|
||||
action_deriveindex=spender
|
||||
@@ -23,11 +42,15 @@ action_ln_pay=spender
|
||||
action_ln_newaddr=spender
|
||||
action_ots_stamp=spender
|
||||
action_ots_getfile=spender
|
||||
action_ln_getinvoice=spender
|
||||
action_ln_decodebolt11=spender
|
||||
action_ln_connectfund=spender
|
||||
|
||||
# Admin can do what the spender can do, plus:
|
||||
|
||||
|
||||
# Should be called from inside the Docker network only:
|
||||
action_conf=internal
|
||||
action_newblock=internal
|
||||
action_executecallbacks=internal
|
||||
action_ots_backoffice=internal
|
||||
|
||||
50
api_auth_docker/auth.sh
Normal file → Executable file
50
api_auth_docker/auth.sh
Normal file → Executable file
@@ -16,20 +16,19 @@
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
verify_sign()
|
||||
{
|
||||
verify_sign() {
|
||||
local returncode
|
||||
|
||||
local header64=$(echo ${1} | cut -sd '.' -f1)
|
||||
local payload64=$(echo ${1} | cut -sd '.' -f2)
|
||||
local signature=$(echo ${1} | cut -sd '.' -f3)
|
||||
local header64=$(echo "${1}" | cut -sd '.' -f1)
|
||||
local payload64=$(echo "${1}" | cut -sd '.' -f2)
|
||||
local signature=$(echo "${1}" | cut -sd '.' -f3)
|
||||
|
||||
trace "[verify_sign] header64=${header64}"
|
||||
trace "[verify_sign] payload64=${payload64}"
|
||||
trace "[verify_sign] signature=${signature}"
|
||||
|
||||
local payload=$(echo -n ${payload64} | base64 -d)
|
||||
local exp=$(echo ${payload} | jq ".exp")
|
||||
local payload=$(echo -n "${payload64}" | base64 -d)
|
||||
local exp=$(echo "${payload}" | jq ".exp")
|
||||
local current=$(date +"%s")
|
||||
|
||||
trace "[verify_sign] payload=${payload}"
|
||||
@@ -38,7 +37,7 @@ verify_sign()
|
||||
|
||||
if [ ${exp} -gt ${current} ]; then
|
||||
trace "[verify_sign] Not expired, let's validate signature"
|
||||
local id=$(echo ${payload} | jq ".id" | tr -d '"')
|
||||
local id=$(echo "${payload}" | jq -r ".id")
|
||||
trace "[verify_sign] id=${id}"
|
||||
|
||||
# Check for code injection
|
||||
@@ -82,38 +81,47 @@ verify_sign()
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_group()
|
||||
{
|
||||
verify_group() {
|
||||
trace "[verify_group] Verifying group..."
|
||||
|
||||
local id=${1}
|
||||
# REQUEST_URI should look like this: /v0/watch/2blablabla
|
||||
local context=$(echo "${REQUEST_URI#\/}" | cut -d '/' -f1)
|
||||
local action=$(echo "${REQUEST_URI#\/}" | cut -d '/' -f2)
|
||||
trace "[verify_group] action=${action}"
|
||||
trace "[verify_group] context=${context} action=${action}"
|
||||
|
||||
# Check for code injection
|
||||
# action can be alphanum... and _ and - but nothing else
|
||||
local actiontoinspect=$(echo "$action" | tr -d '_-')
|
||||
local actiontoinspect=$(echo "$action" | tr -d '_-')
|
||||
case $actiontoinspect in (*[![:alnum:]]*|"")
|
||||
trace "[verify_group] Potential code injection, exiting"
|
||||
return 1
|
||||
esac
|
||||
|
||||
# It is so much faster to include the keys here instead of grep'ing the file for key.
|
||||
. ./api.properties
|
||||
|
||||
local needed_group
|
||||
local ugroups
|
||||
|
||||
eval needed_group='$action_'${action}
|
||||
trace "[verify_group] needed_group=${needed_group}"
|
||||
|
||||
eval ugroups='$ugroups_'$id
|
||||
trace "[verify_group] user groups=${ugroups}"
|
||||
|
||||
case "${ugroups}" in
|
||||
*${needed_group}*) trace "[verify_group] Access granted"; return 0 ;;
|
||||
esac
|
||||
if [ ${context} = "s" ]; then
|
||||
# static files only accessible by a certain group
|
||||
needed_group=${action}
|
||||
elif [ ${context} = "v0" ]; then
|
||||
# actual api calls
|
||||
# It is so much faster to include the keys here instead of grep'ing the file for key.
|
||||
. ./api.properties
|
||||
eval needed_group='$action_'${action}
|
||||
fi
|
||||
|
||||
trace "[verify_group] needed_group=${needed_group}"
|
||||
|
||||
# If needed_group is empty, the action was not found in api.propeties.
|
||||
if [ -n "${needed_group}" ]; then
|
||||
case "${ugroups}" in
|
||||
*${needed_group}*) trace "[verify_group] Access granted"; return 0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
trace "[verify_group] Access NOT granted"
|
||||
return 1
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen 443 ssl;
|
||||
server_name localhost;
|
||||
|
||||
#include /etc/nginx/conf.d/ip-whitelist.conf;
|
||||
ssl_certificate /etc/ssl/certs/cert.pem;
|
||||
ssl_certificate_key /etc/ssl/private/key.pem;
|
||||
|
||||
location /s/ {
|
||||
auth_request /auth;
|
||||
root /etc/nginx/conf.d;
|
||||
}
|
||||
|
||||
location /v0/ {
|
||||
auth_request /auth;
|
||||
proxy_pass http://proxy:8888/;
|
||||
|
||||
# Up default 60 second timeout for 3 minutes (OTS stamping can take time)
|
||||
proxy_connect_timeout 180;
|
||||
proxy_send_timeout 180;
|
||||
proxy_read_timeout 180;
|
||||
send_timeout 180;
|
||||
|
||||
}
|
||||
|
||||
location /auth {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# The file keys.properties generated by the installer should look like this.
|
||||
|
||||
# kapi_id="id";kapi_key="key";kapi_groups="group1,group2";leave the rest intact
|
||||
kapi_id="001";kapi_key="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="002";kapi_key="50c5e483b80964595508f214229b014aa6c013594d57d38bcb841093a39f1d83";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="003";kapi_key="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="004";kapi_key="bb0458b705e774c0c9622efaccfe573aa30c82f62386d9435f04e9727cdc26fd";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="005";kapi_key="6c009201b123e8c24c6b74590de28c0c96f3287e88cac9460a2173a53d73fb87";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="006";kapi_key="19e121b698014fac638f772c4ff5775a738856bf6cbdef0dc88971059c69da4b";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="000";kapi_key="eff1eeea370eacdc5cf7e96c2d82340d1568079a5d4d87006ec8788a98883b36";kapi_groups="stats";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="001";kapi_key="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";kapi_groups="stats,watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="002";kapi_key="50c5e483b80964595508f214229b014aa6c013594d57d38bcb841093a39f1d83";kapi_groups="stats,watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="003";kapi_key="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";kapi_groups="stats,watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="004";kapi_key="bb0458b705e774c0c9622efaccfe573aa30c82f62386d9435f04e9727cdc26fd";kapi_groups="stats,watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="005";kapi_key="6c009201b123e8c24c6b74590de28c0c96f3287e88cac9460a2173a53d73fb87";kapi_groups="stats,watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="006";kapi_key="19e121b698014fac638f772c4ff5775a738856bf6cbdef0dc88971059c69da4b";kapi_groups="stats,watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
installation_status();
|
||||
});
|
||||
|
||||
function httpget(url) {
|
||||
return fetch(url, { method: "GET" })
|
||||
.catch(err => {
|
||||
console.log('HTTP GET Error: ' + err.message + ' :: STACK : ' + err.stack);
|
||||
$("#result").text((JSON.stringify(err.message)));
|
||||
return Promise.reject(err.message);
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
return res.json().then(data => {
|
||||
console.log('HTTP GET Error: ' + data.error.message);
|
||||
$("#result").text(JSON.stringify(data.error.message));
|
||||
return Promise.reject(data.error.message);
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(data => Promise.resolve(JSON.stringify(data)))
|
||||
}
|
||||
|
||||
function installation_status() {
|
||||
httpget("installation.json")
|
||||
.then(result => {
|
||||
$("#result").text(result);
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="hello">
|
||||
<h1>Hello World from Cyphernode!</h1>
|
||||
<h2>If you are here, it means you successfully deployed Cyphernode. Congratulations, fellow Cyphernode Operator!</h2>
|
||||
</div>
|
||||
<hr/>
|
||||
<div id="files">
|
||||
<h2>The following files have been encrypted with your configuration passphrase and your client keys passphrase, respectively:</h2>
|
||||
<ul>
|
||||
<li><a href="config.7z">Download your Cyphernode <b>configurations</b>, can be used for another Cyphernode deployment</a></li>
|
||||
<li><a href="client.7z">Download Client <b>API ID's and keys</b>, needed in your client apps</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="Status">
|
||||
<h2>This is the status of Cyphernode's installation and running components</h2>
|
||||
<pre lang="xml" id="result" style="white-space: pre-wrap"></pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
9
api_auth_docker/trace.sh
Normal file → Executable file
9
api_auth_docker/trace.sh
Normal file → Executable file
@@ -3,13 +3,6 @@
|
||||
trace()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "[$(date +%Y-%m-%dT%H:%M:%S%z)] ${1}" 1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
trace_rc()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "[$(date +%Y-%m-%dT%H:%M:%S%z)] Last return code: ${1}" 1>&2
|
||||
echo "[$(date +%Y-%m-%dT%H:%M:%S%z)] $$ $*" 2>>/var/log/gatekeeper.log 1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
67
build.sh
67
build.sh
@@ -2,6 +2,18 @@
|
||||
|
||||
TRACING=1
|
||||
|
||||
# CYPHERNODE VERSION "v0.2.4"
|
||||
CONF_VERSION="v0.2.4-local"
|
||||
GATEKEEPER_VERSION="v0.2.4-local"
|
||||
PROXY_VERSION="v0.2.4-local"
|
||||
NOTIFIER_VERSION="v0.2.4-local"
|
||||
PROXYCRON_VERSION="v0.2.4-local"
|
||||
OTSCLIENT_VERSION="v0.2.4-local"
|
||||
PYCOIN_VERSION="v0.2.4-local"
|
||||
BITCOIN_VERSION="v0.18.0"
|
||||
LIGHTNING_VERSION="v0.7.1"
|
||||
GRAFANA_VERSION="v0.2.4-local"
|
||||
|
||||
trace()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
@@ -16,60 +28,19 @@ trace_rc()
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
build_docker_image() {
|
||||
|
||||
local dockerfile="Dockerfile"
|
||||
|
||||
if [[ ""$3 != "" ]]; then
|
||||
dockerfile=$3
|
||||
fi
|
||||
|
||||
trace "building docker image: $2"
|
||||
#docker build -q $1 -f $1/$dockerfile -t $2:latest > /dev/null
|
||||
docker build $1 -f $1/$dockerfile -t $2
|
||||
|
||||
}
|
||||
|
||||
build_docker_images() {
|
||||
trace "Updating SatoshiPortal repos"
|
||||
git submodule update --recursive --remote
|
||||
|
||||
local bitcoin_dockerfile=Dockerfile.amd64
|
||||
local clightning_dockerfile=Dockerfile.amd64
|
||||
local proxy_dockerfile=Dockerfile.amd64
|
||||
|
||||
# compat mode for SatoshiPortal repo
|
||||
# TODO: add more mappings?
|
||||
if [[ $(uname -m) == 'armv7l' ]]; then
|
||||
bitcoin_dockerfile="Dockerfile.arm32v6"
|
||||
clightning_dockerfile="Dockerfile.arm32v6"
|
||||
proxy_dockerfile="Dockerfile.arm32v6"
|
||||
fi
|
||||
|
||||
trace "Creating cyphernodeconf image"
|
||||
build_docker_image install/ cyphernode/cyphernodeconf:$CN_VERSION
|
||||
|
||||
trace "Creating SatoshiPortal images"
|
||||
build_docker_image install/SatoshiPortal/dockers/bitcoin-core cyphernode/bitcoin:$BC_VERSION $bitcoin_dockerfile
|
||||
build_docker_image install/SatoshiPortal/dockers/c-lightning cyphernode/clightning:$CL_VERSION $clightning_dockerfile
|
||||
docker build cyphernodeconf_docker/ -t cyphernode/cyphernodeconf:$CONF_VERSION
|
||||
|
||||
trace "Creating cyphernode images"
|
||||
build_docker_image api_auth_docker/ cyphernode/gatekeeper:$CN_VERSION
|
||||
build_docker_image proxy_docker/ cyphernode/proxy:$CN_VERSION $proxy_dockerfile
|
||||
build_docker_image cron_docker/ cyphernode/proxycron:$CN_VERSION
|
||||
build_docker_image pycoin_docker/ cyphernode/pycoin:$CN_VERSION
|
||||
build_docker_image otsclient_docker/ cyphernode/otsclient:$CN_VERSION
|
||||
|
||||
docker build api_auth_docker/ -t cyphernode/gatekeeper:$GATEKEEPER_VERSION \
|
||||
&& docker build proxy_docker/ -t cyphernode/proxy:$PROXY_VERSION \
|
||||
&& docker build notifier_docker/ -t cyphernode/notifier:$NOTIFIER_VERSION \
|
||||
&& docker build cron_docker/ -t cyphernode/proxycron:$PROXYCRON_VERSION \
|
||||
&& docker build pycoin_docker/ -t cyphernode/pycoin:$PYCOIN_VERSION \
|
||||
&& docker build otsclient_docker/ -t cyphernode/otsclient:$OTSCLIENT_VERSION
|
||||
}
|
||||
|
||||
# CYPHERNODE VERSION
|
||||
GATEKEEPER_VERSION="latest"
|
||||
PROXY_VERSION="latest"
|
||||
PROXYCRON_VERSION="latest"
|
||||
OTSCLIENT_VERSION="latest"
|
||||
PYCOIN_VERSION="latest"
|
||||
BITCOIN_VERSION="latest"
|
||||
LIGHTNING_VERSION="latest"
|
||||
|
||||
build_docker_images
|
||||
|
||||
12
cyphernodeconf_docker/.dockerignore
Normal file
12
cyphernodeconf_docker/.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
||||
node_modules
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
.eslintignore
|
||||
.eslintrc.json
|
||||
.gitignore
|
||||
jest.config.js
|
||||
LICENSE
|
||||
build.sh
|
||||
run.sh
|
||||
jest.config.js
|
||||
test
|
||||
4
cyphernodeconf_docker/.eslintignore
Normal file
4
cyphernodeconf_docker/.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
usr
|
||||
scripts
|
||||
config
|
||||
node_modules
|
||||
67
cyphernodeconf_docker/.eslintrc.json
Normal file
67
cyphernodeconf_docker/.eslintrc.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"padding-line-between-statements": [
|
||||
"error",
|
||||
{
|
||||
"blankLine": "always",
|
||||
"prev": "function",
|
||||
"next": "*"
|
||||
}
|
||||
],
|
||||
"newline-per-chained-call": "error",
|
||||
"no-tabs": "error",
|
||||
"no-nested-ternary": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"no-whitespace-before-property": "error",
|
||||
"new-cap": "error",
|
||||
"lines-around-comment": "error",
|
||||
"key-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"brace-style": "error",
|
||||
"camelcase": "error",
|
||||
"handle-callback-err": "error",
|
||||
"no-new-require": "warn",
|
||||
"no-sync": "warn",
|
||||
"no-mixed-requires": "error",
|
||||
"global-require": "error",
|
||||
"comma-spacing": "error",
|
||||
"eqeqeq": "error",
|
||||
"curly": "error",
|
||||
"keyword-spacing": "error",
|
||||
"object-curly-spacing": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"object-curly-newline": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
||||
8
cyphernodeconf_docker/.gitignore
vendored
Normal file
8
cyphernodeconf_docker/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
*~
|
||||
*.log
|
||||
*.sw[a-z]
|
||||
DEADJOE
|
||||
node_modules
|
||||
coverage
|
||||
.DS_Store
|
||||
.idea
|
||||
16
cyphernodeconf_docker/Dockerfile
Normal file
16
cyphernodeconf_docker/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:12.2.0-alpine
|
||||
|
||||
ENV EDITOR=/usr/bin/nano
|
||||
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir /data && \
|
||||
apk add --update su-exec p7zip openssl nano apache2-utils git && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
npm ci --production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT ["/sbin/su-exec"]
|
||||
|
||||
5
cyphernodeconf_docker/build.sh
Executable file
5
cyphernodeconf_docker/build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
VERSION=v0.2.4
|
||||
|
||||
docker build . -t cyphernode/cyphernodeconf:${VERSION}
|
||||
@@ -17,7 +17,9 @@
|
||||
"gatekeeper_edit_apiproperties": "If you know what you are doing, it is possible to manually edit the API endpoints/groups authorization. (Not recommended)",
|
||||
"gatekeeper_apiproperties": "You are about to edit the api.properties file. The format of the file is pretty simple: for each action, you will find what access group can access it. <font color='# 0000ff'>Admin</font> group can do what <font color='# 0000ff'>Spender</font> group can, and <font color='# 0000ff'>Spender</font> group can do what <font color='# 0000ff'>Watcher</font> group can. <font color='# 0000ff'>Internal</font> group is for the endpoints accessible only within the Docker network, like the backoffice tasks used by the Cron container. The access groups for each API id/key are found in the <font color='# 0000ff'>keys.properties</font> file.",
|
||||
"gatekeeper_cns": "I use <font underline='true'>domain names</font> and/or <font underline='true'>IP addresses</font> to create valid TLS certificates. For example, if <font color='# 0000ff'>https://cyphernodehost/getbestblockhash</font> and <font color='# 0000ff'>https://192.168.7.44/getbestblockhash</font> will be used, enter <font color='# 0000ff'>cyphernodehost, 192.168.7.44</font> as a possible domains. <font color='# 0000ff'>127.0.0.1, localhost, gatekeeper</font> will be automatically added to your list. Make sure the provided domain names are in your DNS or client's hosts file and is reachable.",
|
||||
"bitcoin_mode": "Cyphernode can spawn a new <font underline='true'>Bitcoin Core</font> full node for its own use. But if you already have a Bitcoin Core node running, Cyphernode can use that.",
|
||||
"traefik_datapath": "The Traefik's files will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
|
||||
"traefik_datapath_custom": "Provide the <font underline='true'>full path name</font> where the Traefik's files will be saved.",
|
||||
"bitcoin_mode": "Cyphernode will spawn a new <font underline='true'>Bitcoin Core</font> full node for its own use. If you already have Bitcoin Core node data, you can use the directory containing that data directly or copy the contents of it to a new directory to be used by cyphernode. Be aware that the files might change ownership, if you run cyphernode as a different user. In case you want to move the blockchain data to another node you might need to change the owner to fit the configuration of that node.",
|
||||
"bitcoin_node_ip": "Cyphernode uses <font color='#00ff00'>Bitcoin Core</font> RPC interface for its tasks. Please provide the <font underline='true'>IP address</font> of your current Bitcoin Core node.",
|
||||
"bitcoin_rpcuser": "Bitcoin Core's <font underline='true'>RPC username</font> used by Cyphernode when calling the node.",
|
||||
"bitcoin_rpcpassword": "Bitcoin Core's <font underline='true'>RPC password</font> used by Cyphernode when calling the node.",
|
||||
@@ -28,6 +30,7 @@
|
||||
"bitcoin_datapath_custom": " ",
|
||||
"bitcoin_expose": "By default, Bitcoin node ports (RPC and protocol) won't be <font underline='true'>published</font> outside of Docker. Do you want to expose them so that your node can be accessed from outside of the Docker network?",
|
||||
"lightning_implementation": "Multiple <font underline='true'>LN implementations</font> exist. Please choose the one you want to use with Cyphernode.",
|
||||
"lightning_external_ip": "If you want you LN node to be accessible from the Internet, provide the <font underline='true'>IP address</font> that other LN nodes will use to connect to it. This is usually your router's public IP. <font color='#00ff00'>NOTE</font>: In case you are running Cyphernode at home. This option won't make your router forward needed LN ports; you still need to configure and manage that part yourself in your router configuration.",
|
||||
"lightning_nodename": "LN nodes have names. Choose the <font underline='true'>name you want</font> for yours.",
|
||||
"lightning_nodecolor": "LN nodes have colors. Choose the <font underline='true'>color you want</font> for yours in RGB format (RRGGBB). For example, pure red would be <font color='#ff0000'>ff0000</font>.",
|
||||
"lightning_datapath": "<font underline='true'>Path name</font> to where LN's data files are stored. This directory will be mounted into the LN node's container. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
|
||||
12
cyphernodeconf_docker/index.js
Normal file
12
cyphernodeconf_docker/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const App = require( './lib/app.js' );
|
||||
|
||||
const main = async ( args ) => {
|
||||
const app = new App();
|
||||
const noWizard = args.indexOf('recreate') !== -1;
|
||||
await app.start( {
|
||||
noWizard: noWizard,
|
||||
noSplashScreen: noWizard
|
||||
} );
|
||||
};
|
||||
|
||||
main( process.argv.slice( 2, process.argv.length ) );
|
||||
185
cyphernodeconf_docker/jest.config.js
Normal file
185
cyphernodeconf_docker/jest.config.js
Normal file
@@ -0,0 +1,185 @@
|
||||
// For a detailed explanation regarding each configuration property, visit:
|
||||
// https://jestjs.io/docs/en/configuration.html
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// Respect "browser" field in package.json when resolving modules
|
||||
// browser: false,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/fb/q84grm8d0w3gz0y0yx_bvx700000gn/T/jest_dx",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: true,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: null,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
coverageReporters: [
|
||||
//"json",
|
||||
"text",
|
||||
//"lcov",
|
||||
//"clover"
|
||||
],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: null,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: null,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files usin a array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: null,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: null,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: null,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: null,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: null,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: null,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
// testMatch: [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: null,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: null,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: null,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
||||
4
cyphernodeconf_docker/lib/ansi.js
Normal file
4
cyphernodeconf_docker/lib/ansi.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
reset: '\u001B8\u001B[u',
|
||||
clear: '\u001Bc'
|
||||
};
|
||||
@@ -1,13 +1,24 @@
|
||||
const spawn = require('child_process').spawn;
|
||||
const configEntryRegexp = /^kapi_id="(\w+)";kapi_key="(\w+)";kapi_groups="(.+)";(.+)$/;
|
||||
|
||||
module.exports = class ApiKey {
|
||||
constructor( id, groups, key, script ) {
|
||||
this.setId(id || '001');
|
||||
this.setGroups(groups || ['admin'] );
|
||||
this.setId(id || '000');
|
||||
this.setGroups(groups || ['stats'] );
|
||||
this.setScript(script || 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' );
|
||||
this.setKey(key);
|
||||
}
|
||||
|
||||
setFromConfigEntry(configEntry ) {
|
||||
const match = configEntryRegexp.exec( configEntry );
|
||||
if( match ) {
|
||||
this.setId( match[1] );
|
||||
this.setKey( match[2] );
|
||||
this.setGroups( match[3].split(',').map( (e)=>e.trim() ) )
|
||||
this.setScript( match[4] );
|
||||
}
|
||||
}
|
||||
|
||||
setGroups( groups ) {
|
||||
this.groups = groups;
|
||||
}
|
||||
@@ -44,15 +55,13 @@ module.exports = class ApiKey {
|
||||
});
|
||||
|
||||
dd.stdout.on('error', function(err) {
|
||||
console.log(err);
|
||||
reject(err);
|
||||
})
|
||||
});
|
||||
this.key = result;
|
||||
|
||||
} catch( err ) {
|
||||
console.log( err );
|
||||
return;
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +80,4 @@ module.exports = class ApiKey {
|
||||
return `${this.id}=${this.key}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32
|
||||
};
|
||||
646
cyphernodeconf_docker/lib/app.js
Normal file
646
cyphernodeconf_docker/lib/app.js
Normal file
@@ -0,0 +1,646 @@
|
||||
const {promisify} = require('util');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const wrap = require('wrap-ansi');
|
||||
const validator = require('validator');
|
||||
const coinstring = require('coinstring');
|
||||
const inquirer = require('inquirer');
|
||||
const colorsys = require('colorsys');
|
||||
const ejs = require( 'ejs' );
|
||||
const ejsRenderFileAsync = promisify( ejs.renderFile ).bind( ejs );
|
||||
|
||||
const html2ansi = require('./html2ansi.js');
|
||||
const name = require('./name.js');
|
||||
const Archive = require('./archive.js');
|
||||
const ApiKey = require('./apikey.js');
|
||||
const Cert = require('./cert.js');
|
||||
const htpasswd = require( './htpasswd.js');
|
||||
const Config = require('./config.js');
|
||||
const SplashScreen = require( './splashScreen.js' );
|
||||
const ansi = require( './ansi.js' );
|
||||
|
||||
const features = require('../features.json');
|
||||
|
||||
const uaCommentRegexp = /^[a-zA-Z0-9 \.,:_\-\?\/@]+$/; // TODO: look for spec of unsafe chars
|
||||
const userRegexp = /^[a-zA-Z0-9\._\-]+$/;
|
||||
const maxWidth = 82;
|
||||
|
||||
const keyIds = {
|
||||
'000': ['stats'],
|
||||
'001': ['stats','watcher'],
|
||||
'002': ['stats','watcher','spender'],
|
||||
'003': ['stats','watcher','spender','admin']
|
||||
};
|
||||
|
||||
const configArchiveFileName = 'config.7z';
|
||||
const keyArchiveFileName = 'client.7z';
|
||||
const destinationDirName = '.cyphernodeconf';
|
||||
|
||||
const prefix = () => {
|
||||
return chalk.green('Cyphernode')+': ';
|
||||
};
|
||||
|
||||
const randomColor = () => {
|
||||
const hex = colorsys.hslToHex( { h: (Math.random()*360)<<0, s: 50, l: 50 } );
|
||||
return hex.substr(1);
|
||||
};
|
||||
|
||||
let prompters = [];
|
||||
fs.readdirSync(path.join(__dirname, '..','prompters')).forEach((file) => {
|
||||
prompters.push(require(path.join(__dirname, '..','prompters',file)));
|
||||
});
|
||||
|
||||
module.exports = class App {
|
||||
|
||||
constructor() {
|
||||
this.features = features;
|
||||
|
||||
if( fs.existsSync(path.join('/data', destinationDirName, 'exitStatus.sh')) ) {
|
||||
fs.unlinkSync(path.join('/data', destinationDirName, 'exitStatus.sh'));
|
||||
}
|
||||
|
||||
this.splash = new SplashScreen( {
|
||||
frameDir: path.join(__dirname, '..', 'splash' ),
|
||||
enableFortune: false,
|
||||
fortuneChalk: chalk.default.bold,
|
||||
width: maxWidth,
|
||||
fortuneSpacing: 3
|
||||
} );
|
||||
}
|
||||
|
||||
async start( options ) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.sessionData = {
|
||||
defaultDataDirBase: process.env.DEFAULT_DATADIR_BASE || process.env.HOME,
|
||||
setupDir: process.env.SETUP_DIR || path.join( process.env.HOME, 'cyphernode' ),
|
||||
default_username: process.env.DEFAULT_USER || '',
|
||||
gatekeeper_version: process.env.GATEKEEPER_VERSION,
|
||||
gatekeeper_cns: process.env.DEFAULT_CERT_HOSTNAME,
|
||||
proxy_version: process.env.PROXY_VERSION,
|
||||
proxycron_version: process.env.PROXYCRON_VERSION,
|
||||
pycoin_version: process.env.PYCOIN_VERSION,
|
||||
otsclient_version: process.env.OTSCLIENT_VERSION,
|
||||
bitcoin_version: process.env.BITCOIN_VERSION,
|
||||
lightning_version: process.env.LIGHTNING_VERSION,
|
||||
notifier_version: process.env.NOTIFIER_VERSION,
|
||||
setup_version: process.env.SETUP_VERSION,
|
||||
noWizard: !!options.noWizard,
|
||||
noSplashScreen: !!options.noSplashScreen,
|
||||
lightning_nodename: name.generate(),
|
||||
lightning_nodecolor: randomColor(),
|
||||
installer_cleanup: false,
|
||||
devmode: process.env.DEVMODE || false
|
||||
};
|
||||
|
||||
await this.setupConfigArchive();
|
||||
|
||||
if( !this.sessionData.noSplashScreen ) {
|
||||
await this.splash.show();
|
||||
}
|
||||
|
||||
let missingProperties = [];
|
||||
|
||||
if( this.config.validateErrors && this.config.validateErrors.length ) {
|
||||
for( let error of this.config.validateErrors ) {
|
||||
if( error.keyword === 'required' && error.params && error.params.missingProperty ) {
|
||||
missingProperties.push( error.params.missingProperty );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( this.sessionData.noWizard && missingProperties.length && this.config.isLoaded ) {
|
||||
console.log(chalk.bold.red('Unable to migrate client.7z non-interactively. Rerun without the -r option') );
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if( !this.sessionData.noWizard ) {
|
||||
// save gatekeeper key password to check if it changed
|
||||
this.sessionData.gatekeeper_clientkeyspassword = this.config.data.gatekeeper_clientkeyspassword;
|
||||
if( missingProperties.length && this.config.isLoaded ) {
|
||||
this.sessionData.markProperties = missingProperties;
|
||||
}
|
||||
await this.startWizard();
|
||||
}
|
||||
|
||||
await this.processProps();
|
||||
await this.writeFiles();
|
||||
}
|
||||
|
||||
async setupConfigArchive() {
|
||||
|
||||
this.config = new Config( {
|
||||
setup_version: this.sessionData.setup_version,
|
||||
docker_versions: {
|
||||
'cyphernode/bitcoin': this.sessionData.bitcoin_version,
|
||||
'cyphernode/gatekeeper': this.sessionData.gatekeeper_version,
|
||||
'cyphernode/proxy': this.sessionData.proxy_version,
|
||||
'cyphernode/proxycron': this.sessionData.proxycron_version,
|
||||
'cyphernode/pycoin': this.sessionData.pycoin_version,
|
||||
'cyphernode/otsclient': this.sessionData.otsclient_version,
|
||||
'cyphernode/clightning': this.sessionData.lightning_version,
|
||||
'cyphernode/notifier': this.sessionData.notifier_version,
|
||||
'traefik': 'v1.7.9-alpine',
|
||||
'eclipse-mosquitto': '1.6'
|
||||
}
|
||||
} );
|
||||
|
||||
if( !fs.existsSync(this.destinationPath(configArchiveFileName)) ) {
|
||||
let r = {};
|
||||
process.stdout.write(ansi.clear+ansi.reset);
|
||||
while( !r.password0 || !r.password1 || r.password0 !== r.password1 ) {
|
||||
|
||||
if( r.password0 && r.password1 && r.password0 !== r.password1 ) {
|
||||
console.log(chalk.bold.red('Passwords do not match')+'\n');
|
||||
}
|
||||
|
||||
r = await this.prompt([{
|
||||
type: 'password',
|
||||
name: 'password0',
|
||||
message: prefix()+chalk.bold.blue('Choose your configuration password'),
|
||||
filter: this.trimFilter
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password1',
|
||||
message: prefix()+chalk.bold.blue('Confirm your configuration password'),
|
||||
filter: this.trimFilter
|
||||
}]);
|
||||
}
|
||||
this.sessionData.configurationPassword = r.password0;
|
||||
} else {
|
||||
try {
|
||||
let r = {};
|
||||
if( process.env.CFG_PASSWORD ) {
|
||||
this.sessionData.configurationPassword = process.env.CFG_PASSWORD;
|
||||
} else {
|
||||
process.stdout.write(ansi.reset);
|
||||
while( !r.password ) {
|
||||
r = await this.prompt([{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: prefix()+chalk.bold.blue('Enter your configuration password?'),
|
||||
filter: this.trimFilter
|
||||
}]);
|
||||
}
|
||||
this.sessionData.configurationPassword = r.password;
|
||||
}
|
||||
try {
|
||||
await this.config.deserialize(
|
||||
this.destinationPath(configArchiveFileName),
|
||||
this.sessionData.configurationPassword,
|
||||
);
|
||||
|
||||
// store clientkeyspassword in sessionData so it can be retrieved by getDefault
|
||||
// and a simple return will not result in a password mismatch
|
||||
if( this.config.data.hasOwnProperty('gatekeeper_clientkeyspassword') ) {
|
||||
this.sessionData.gatekeeper_clientkeyspassword_c =
|
||||
this.config.data.gatekeeper_clientkeyspassword;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(chalk.bold.red(e));
|
||||
process.exit();
|
||||
}
|
||||
} catch( err ) {
|
||||
console.log(chalk.bold.red('config archive is corrupt.'));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
this.config.data.adminhash = await htpasswd(this.sessionData.configurationPassword);
|
||||
|
||||
for( let feature of this.features ) {
|
||||
feature.checked = this.isChecked( 'features', feature.value );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async startWizard() {
|
||||
let r = await this.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'enablehelp',
|
||||
message: prefix()+'Enable help?',
|
||||
default: this.getDefault( 'enablehelp' ),
|
||||
}]);
|
||||
|
||||
this.config.data.enablehelp = r.enablehelp;
|
||||
|
||||
if( this.config.data.enablehelp ) {
|
||||
this.help = require('../help.json');
|
||||
}
|
||||
|
||||
let prompts = [];
|
||||
|
||||
for( let m of prompters ) {
|
||||
let newPrompts = m.prompts(this);
|
||||
|
||||
if( this.sessionData.markProperties &&
|
||||
this.sessionData.markProperties.length &&
|
||||
this.config.isLoaded ) {
|
||||
|
||||
for( let prompt of newPrompts ) {
|
||||
if( this.sessionData.markProperties.indexOf(prompt.name) !== -1 ) {
|
||||
prompt.message = prompt.message+' '+chalk.bgGreen('new option');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prompts = prompts.concat(newPrompts);
|
||||
}
|
||||
|
||||
const props = await this.prompt(prompts);
|
||||
|
||||
this.config.data = Object.assign(this.config.data, props);
|
||||
}
|
||||
|
||||
async processProps() {
|
||||
|
||||
// creates keys if they don't exist or we say so.
|
||||
if( this.config.data.gatekeeper_recreatekeys ||
|
||||
this.config.data.gatekeeper_keys.configEntries.length===0 ) {
|
||||
|
||||
delete this.config.data.gatekeeper_recreatekeys;
|
||||
|
||||
let configEntries = [];
|
||||
let clientInformation = [];
|
||||
|
||||
for( let keyId in keyIds ) {
|
||||
const apikey = await this.createRandomKey( keyId, keyIds[keyId] );
|
||||
configEntries.push(apikey.getConfigEntry());
|
||||
clientInformation.push(apikey.getClientInformation());
|
||||
}
|
||||
|
||||
this.config.data.gatekeeper_keys = {
|
||||
configEntries: configEntries,
|
||||
clientInformation: clientInformation
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const cert = new Cert();
|
||||
this.sessionData.cns = cert.cns(this.config.data.gatekeeper_cns);
|
||||
|
||||
// create certs if they don't exist or we say so.
|
||||
if( this.config.data.gatekeeper_recreatecert ||
|
||||
!this.config.data.gatekeeper_sslcert ||
|
||||
!this.config.data.gatekeeper_sslkey ) {
|
||||
delete this.config.data.gatekeeper_recreatecert;
|
||||
const cert = new Cert();
|
||||
console.log(chalk.bold.green( '☕ Generating gatekeeper cert. This may take a while ☕' ));
|
||||
try {
|
||||
const result = await cert.create(this.sessionData.cns);
|
||||
if( result.code === 0 ) {
|
||||
this.config.data.gatekeeper_sslkey = result.key.toString();
|
||||
this.config.data.gatekeeper_sslcert = result.cert.toString();
|
||||
} else {
|
||||
console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' ));
|
||||
}
|
||||
} catch( err ) {
|
||||
console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createRandomKey( id, groups ) {
|
||||
if( !id || !groups || !groups.length ) {
|
||||
return;
|
||||
}
|
||||
const apikey = new ApiKey();
|
||||
apikey.setId(id);
|
||||
apikey.setGroups(groups);
|
||||
await apikey.randomiseKey();
|
||||
return apikey
|
||||
}
|
||||
|
||||
async writeFiles() {
|
||||
|
||||
console.log( chalk.green( ' create' )+' '+configArchiveFileName );
|
||||
if( !this.config.serialize(
|
||||
this.destinationPath(configArchiveFileName),
|
||||
this.sessionData.configurationPassword
|
||||
) ) {
|
||||
console.log(chalk.bold.red( 'error! Config archive was not written' ));
|
||||
}
|
||||
|
||||
const pathProps = [
|
||||
'gatekeeper_datapath',
|
||||
'traefik_datapath',
|
||||
'proxy_datapath',
|
||||
'bitcoin_datapath',
|
||||
'lightning_datapath',
|
||||
'otsclient_datapath'
|
||||
];
|
||||
|
||||
for( let pathProp of pathProps ) {
|
||||
if( this.config.data[pathProp] === '_custom' ) {
|
||||
this.config.data[pathProp] = this.config.data[pathProp+'_custom'] || '';
|
||||
}
|
||||
}
|
||||
|
||||
this.sessionData.installationInfo = this.installationInfo();
|
||||
|
||||
for( let m of prompters ) {
|
||||
const name = m.name();
|
||||
for( let t of m.templates(this.config.data) ) {
|
||||
const p = path.join(name,t);
|
||||
const destFile = this.destinationPath( path.join( destinationDirName, p ) );
|
||||
const targetDir = path.dirname( destFile );
|
||||
|
||||
if( !fs.existsSync(targetDir) ) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
const result = await ejsRenderFileAsync( this.templatePath(p), Object.assign({}, this.sessionData, this.config.data), {} );
|
||||
|
||||
console.log( chalk.green( ' create' )+' '+p );
|
||||
fs.writeFileSync( destFile, result );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
console.log( chalk.green( ' create' )+' '+keyArchiveFileName );
|
||||
|
||||
if( this.config.data.gatekeeper_keys && this.config.data.gatekeeper_keys.clientInformation ) {
|
||||
|
||||
if( this.sessionData.gatekeeper_clientkeyspassword !== this.config.data.gatekeeper_clientkeyspassword &&
|
||||
fs.existsSync(this.destinationPath(keyArchiveFileName)) ) {
|
||||
fs.unlinkSync( this.destinationPath(keyArchiveFileName) );
|
||||
}
|
||||
|
||||
const archive = new Archive( this.destinationPath(keyArchiveFileName), this.config.data.gatekeeper_clientkeyspassword );
|
||||
if( !await archive.writeEntry( 'keys.txt', this.config.data.gatekeeper_keys.clientInformation.join('\n') ) ) {
|
||||
console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' ));
|
||||
}
|
||||
if( !await archive.writeEntry( 'cacert.pem', this.config.data.gatekeeper_sslcert ) ) {
|
||||
console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' ));
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join('/data', destinationDirName, 'exitStatus.sh'), 'EXIT_STATUS=0');
|
||||
|
||||
}
|
||||
|
||||
installationInfo() {
|
||||
|
||||
for( let feature of this.features ) {
|
||||
feature.checked = this.isChecked( 'features', feature.value );
|
||||
}
|
||||
|
||||
const cert = new Cert();
|
||||
const gatekeeper_cns = cert.cns( this.config.data.gatekeeper_cns );
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Bitcoin core node',
|
||||
label: 'bitcoin',
|
||||
host: 'bitcoin',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'cyphernode/bitcoin:'+this.config.docker_versions['cyphernode/bitcoin'],
|
||||
extra: {
|
||||
prune: this.config.data.bitcoin_prune,
|
||||
prune_size: this.config.data.bitcoin_prune_size,
|
||||
expose: this.config.data.bitcoin_expose,
|
||||
uacomment: this.config.data.bitcoin_uacomment
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Gatekeeper',
|
||||
label: 'gatekeeper',
|
||||
host: 'gatekeeper',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'cyphernode/gatekeeper:'+this.config.docker_versions['cyphernode/gatekeeper'],
|
||||
extra: {
|
||||
port: this.config.data.gatekeeper_port,
|
||||
cns: gatekeeper_cns
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Proxy',
|
||||
label: 'proxy',
|
||||
host: 'proxy',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'cyphernode/proxy:'+this.config.docker_versions['cyphernode/proxy']
|
||||
},
|
||||
{
|
||||
name: 'Proxy cron',
|
||||
label: 'proxycron',
|
||||
host: 'proxycron',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'cyphernode/proxycron:'+this.config.docker_versions['cyphernode/proxycron']
|
||||
},
|
||||
{
|
||||
name: 'Pycoin',
|
||||
label: 'pycoin',
|
||||
host: 'pycoin',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'cyphernode/pycoin:'+this.config.docker_versions['cyphernode/pycoin']
|
||||
},
|
||||
{
|
||||
name: 'Notifier',
|
||||
label: 'notifier',
|
||||
host: 'notifier',
|
||||
networks: ['cyphernodenet', 'cyphernodeappsnet'],
|
||||
docker: 'cyphernode/notifier:'+this.config.docker_versions['cyphernode/notifier']
|
||||
},
|
||||
{
|
||||
name: 'MQ broker',
|
||||
label: 'broker',
|
||||
host: 'broker',
|
||||
networks: ['cyphernodenet'],
|
||||
docker: 'eclipse-mosquitto:'+this.config.docker_versions['eclipse-mosquitto']
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
const optional_features = [];
|
||||
|
||||
const optional_features_data = {
|
||||
otsclient: {
|
||||
docker: "cyphernode/otsclient:" + this.config.docker_versions['cyphernode/otsclient']
|
||||
},
|
||||
lightning: {
|
||||
docker: "cyphernode/clightning:"+this.config.docker_versions['cyphernode/clightning'],
|
||||
extra: {
|
||||
nodename: this.config.data.lightning_nodename,
|
||||
nodecolor: this.config.data.lightning_nodecolor,
|
||||
expose: this.config.data.lightning_expose,
|
||||
external_ip: this.config.data.lightning_external_ip,
|
||||
implementation: this.config.data.lightning_implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( let feature of this.features ) {
|
||||
const f = {
|
||||
active: feature.checked,
|
||||
name: feature.name,
|
||||
label: feature.value,
|
||||
host: feature.value,
|
||||
networks: ['cyphernodenet'],
|
||||
docker: optional_features_data[feature.value].docker
|
||||
};
|
||||
|
||||
if( feature.checked ) {
|
||||
f.extra = optional_features_data[feature.value].extra
|
||||
}
|
||||
|
||||
optional_features.push( f );
|
||||
}
|
||||
|
||||
let bitcoin_version = this.config.docker_versions['cyphernode/bitcoin'];
|
||||
|
||||
if( bitcoin_version[0] === 'v' ) {
|
||||
bitcoin_version = bitcoin_version.substr(1);
|
||||
}
|
||||
|
||||
const ii = {
|
||||
api_versions: ['v0'],
|
||||
setup_version: this.config.setup_version,
|
||||
bitcoin_version: bitcoin_version,
|
||||
features: features,
|
||||
optional_features: optional_features,
|
||||
devmode: this.sessionData.devmode,
|
||||
|
||||
};
|
||||
|
||||
return ii;
|
||||
}
|
||||
|
||||
async prompt( questions ) {
|
||||
if( !questions ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const r = await inquirer.prompt( questions );
|
||||
return r;
|
||||
}
|
||||
|
||||
/* some utils */
|
||||
|
||||
destinationPath( relPath ) {
|
||||
return path.join( '/data', relPath );
|
||||
}
|
||||
|
||||
templatePath( relPath ) {
|
||||
return path.join(__dirname, '..','templates',relPath );
|
||||
}
|
||||
|
||||
isChecked(name, value ) {
|
||||
return this.config.data && this.config.data[name] && this.config.data[name].indexOf(value) != -1 ;
|
||||
}
|
||||
|
||||
getDefault(name) {
|
||||
|
||||
if( this.config && this.config.data && this.config.data.hasOwnProperty(name) ) {
|
||||
return this.config.data[name];
|
||||
}
|
||||
|
||||
if( this.sessionData && this.sessionData.hasOwnProperty(name) ) {
|
||||
return this.sessionData[name];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
optional(input, validator) {
|
||||
if( input === undefined ||
|
||||
input === null ||
|
||||
input === '' ) {
|
||||
return true;
|
||||
}
|
||||
return validator(input);
|
||||
}
|
||||
|
||||
ipOrFQDNValidator(host ) {
|
||||
host = (host+"").trim();
|
||||
if( !(validator.isIP(host) ||
|
||||
validator.isFQDN(host)) ) {
|
||||
throw new Error( 'No IP address or fully qualified domain name' )
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
xkeyValidator(xpub ) {
|
||||
// TOOD: check for version
|
||||
if( !coinstring.isValid( xpub ) ) {
|
||||
throw new Error('Not an extended key.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pathValidator(p ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
derivationPathValidator(path ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
colorValidator(color) {
|
||||
if( !validator.isHexadecimal(color) ) {
|
||||
throw new Error('Not a hex color.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
lightningNodeNameValidator(name) {
|
||||
if( !name || name.length > 32 ) {
|
||||
throw new Error('Please enter anything shorter than 32 characters');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
notEmptyValidator(path ) {
|
||||
if( !path ) {
|
||||
throw new Error('Please enter something');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
usernameValidator(user ) {
|
||||
if( !userRegexp.test( user ) ) {
|
||||
throw new Error('Choose a valid username');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
UACommentValidator(comment ) {
|
||||
if( !uaCommentRegexp.test( comment ) ) {
|
||||
throw new Error('Unsafe characters in UA comment. Please use only a-z, A-Z, 0-9, SPACE and .,:_?@');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
trimFilter(input ) {
|
||||
return (input+"").trim();
|
||||
}
|
||||
|
||||
featureChoices() {
|
||||
return this.features;
|
||||
}
|
||||
|
||||
setupDir() {
|
||||
return this.sessionData.setupDir;
|
||||
}
|
||||
|
||||
defaultDataDirBase() {
|
||||
return this.sessionData.defaultDataDirBase;
|
||||
}
|
||||
|
||||
getHelp(topic ) {
|
||||
if( !this.config.data.enablehelp || !this.help ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const helpText = this.help[topic] || this.help['__default__'];
|
||||
|
||||
if( !helpText ||helpText === '' ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return "\n\n"+wrap( html2ansi(helpText),maxWidth )+"\n\n";
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
const fs = require('fs');
|
||||
const spawn = require('child_process').spawn;
|
||||
const stringio = require('@rauschma/stringio');
|
||||
const defaultArgs = ['-t7z', '-ms=on', '-mhe=on'];
|
||||
@@ -16,7 +15,7 @@ module.exports = class Archive {
|
||||
}
|
||||
let args = defaultArgs.slice();
|
||||
args.unshift('x');
|
||||
args.push( '-so' );
|
||||
args.push( '-so' );
|
||||
if( this.password ) {
|
||||
args.push('-p'+this.password );
|
||||
}
|
||||
@@ -41,7 +40,7 @@ module.exports = class Archive {
|
||||
if( this.password ) {
|
||||
args.push('-p'+this.password );
|
||||
}
|
||||
args.push( '-si'+entryName );
|
||||
args.push( '-si'+entryName );
|
||||
args.push( this.file )
|
||||
const archiver = spawn('7z', args, { stdio: ['pipe', 'ignore', 'ignore' ] } );
|
||||
await stringio.streamWrite(archiver.stdin, content);
|
||||
@@ -20,7 +20,9 @@ subjectAltName = @alt_names
|
||||
`;
|
||||
|
||||
const domainTmpl = 'DNS.%#% = %DOMAIN%';
|
||||
const ipTmpl = 'IP.%#% = %IP%'
|
||||
const ipTmpl = 'IP.%#% = %IP%';
|
||||
|
||||
const standardCNs = ['127.0.0.1','localhost','gatekeeper'];
|
||||
|
||||
module.exports = class Cert {
|
||||
|
||||
@@ -29,6 +31,18 @@ module.exports = class Cert {
|
||||
this.args = options.args || { days: 3650 };
|
||||
}
|
||||
|
||||
cns( cnsString ) {
|
||||
if( !cnsString ) {
|
||||
return standardCNs;
|
||||
}
|
||||
const cns = cnsString.split(',').map(e=>e.trim().toLowerCase()).filter(e=>!!e);
|
||||
|
||||
if( cns.length ) {
|
||||
return standardCNs.concat(cns);
|
||||
}
|
||||
return standardCNs;
|
||||
}
|
||||
|
||||
buildConfig( cns ) {
|
||||
|
||||
let ips = [];
|
||||
@@ -62,9 +76,9 @@ module.exports = class Cert {
|
||||
}
|
||||
|
||||
async create( cns ) {
|
||||
cns = cns || [];
|
||||
|
||||
cns = cns.concat(['127.0.0.1','localhost','gatekeeper']);
|
||||
if (!cns || !cns.length) {
|
||||
throw 'No cns';
|
||||
}
|
||||
|
||||
let args = defaultArgs.slice();
|
||||
|
||||
@@ -87,9 +101,9 @@ module.exports = class Cert {
|
||||
const conf = this.buildConfig( cns );
|
||||
fs.writeFileSync( confFileTmp.name, conf );
|
||||
|
||||
const openssl = spawn('openssl', args, { stdio: ['ignore', 'ignore', 'ignore'] } );
|
||||
|
||||
let code = await new Promise( function(resolve, reject) {
|
||||
const openssl = spawn('openssl', args, { stdio: ['ignore', 'ignore', 'ignore'] } );
|
||||
openssl.on('exit', (code) => {
|
||||
resolve(code);
|
||||
});
|
||||
@@ -105,29 +119,7 @@ module.exports = class Cert {
|
||||
return {
|
||||
code: code,
|
||||
key: key,
|
||||
cert: cert,
|
||||
cns: cns
|
||||
cert: cert
|
||||
}
|
||||
}
|
||||
|
||||
getFullPath() {
|
||||
return path.join( this.folder, this.filename );
|
||||
}
|
||||
|
||||
async passwd( pw ) {
|
||||
const openssl = spawn('openssl', [ "passwd", pw ], {stdio: ['ignore', 'pipe', 'ignore' ]});
|
||||
|
||||
const result = await new Promise( function(resolve, reject ) {
|
||||
let result = '';
|
||||
openssl.stdout.on('data', (data) => {
|
||||
result += data.toString();
|
||||
});
|
||||
|
||||
openssl.on('exit', (code) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
220
cyphernodeconf_docker/lib/config.js
Normal file
220
cyphernodeconf_docker/lib/config.js
Normal file
@@ -0,0 +1,220 @@
|
||||
const Ajv = require('ajv');
|
||||
const fs = require('fs');
|
||||
const Archive = require('./archive.js');
|
||||
const ApiKey = require('./apikey.js');
|
||||
const name = require('./name.js');
|
||||
const colorsys = require( 'colorsys');
|
||||
|
||||
|
||||
const schemas = {
|
||||
'0.1.0': require('../schema/config-v0.1.0.json'),
|
||||
'0.2.0': require('../schema/config-v0.2.0.json'),
|
||||
'0.2.2': require('../schema/config-v0.2.2.json')
|
||||
};
|
||||
|
||||
const versionHistory = [ '0.1.0', '0.2.0', '0.2.2' ];
|
||||
const defaultSchemaVersion=versionHistory[0];
|
||||
const latestSchemaVersion=versionHistory[versionHistory.length-1];
|
||||
|
||||
module.exports = class Config {
|
||||
|
||||
constructor( options ) {
|
||||
|
||||
options = options || {};
|
||||
this.setup_version = options.setup_version;
|
||||
this.docker_versions = options.docker_versions;
|
||||
|
||||
const ajv = new Ajv({
|
||||
removeAdditional: true,
|
||||
useDefaults: true,
|
||||
coerceTypes: true,
|
||||
allErrors: true
|
||||
});
|
||||
|
||||
this.validators = {};
|
||||
|
||||
for( let v in schemas ) {
|
||||
this.validators[v]=ajv.compile(schemas[v]);
|
||||
}
|
||||
|
||||
|
||||
this.migrations = {
|
||||
'0.1.0->0.2.0': this.migrate_0_1_0_to_0_2_0,
|
||||
'0.2.0->0.2.2': this.migrate_0_2_0_to_0_2_2
|
||||
};
|
||||
|
||||
this.setData( { schema_version: latestSchemaVersion } );
|
||||
this.isLoaded = false;
|
||||
}
|
||||
|
||||
generateMigrationPathToLatest( currentVersion ) {
|
||||
if( currentVersion === latestSchemaVersion ) {
|
||||
return;
|
||||
}
|
||||
const index = versionHistory.indexOf( currentVersion );
|
||||
if( index === -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let path = [];
|
||||
for( let i=index; i<versionHistory.length-1; i++ ) {
|
||||
const methodLabel = versionHistory[i]+'->'+versionHistory[i+1];
|
||||
path.push( this.migrations[methodLabel] );
|
||||
}
|
||||
|
||||
return path;
|
||||
|
||||
}
|
||||
|
||||
setData( data ) {
|
||||
if( !data ) {
|
||||
return;
|
||||
}
|
||||
this.data = data;
|
||||
this.data.schema_version = this.data.schema_version || this.__version || defaultSchemaVersion;
|
||||
this.data.setup_version = this.data.setup_version || this.setup_version;
|
||||
this.data.docker_versions = this.data.docker_versions || this.docker_versions;
|
||||
this.validate();
|
||||
}
|
||||
|
||||
async serialize( path, password ) {
|
||||
this.resolveConfigConflicts();
|
||||
this.validate();
|
||||
const configJsonString = JSON.stringify(this.data, null, 4);
|
||||
const archive = new Archive( path, password );
|
||||
return archive.writeEntry( 'config.json', configJsonString );
|
||||
}
|
||||
|
||||
async deserialize( path, password ) {
|
||||
|
||||
if( fs.existsSync(path) ) {
|
||||
|
||||
const archive = new Archive( path, password );
|
||||
|
||||
const r = await archive.readEntry('config.json');
|
||||
|
||||
if( r.error ) {
|
||||
throw( 'Password is wrong. Have a nice day.' );
|
||||
}
|
||||
|
||||
if( !r.value ) {
|
||||
throw('config archive is corrupt.');
|
||||
}
|
||||
|
||||
this.setData( JSON.parse(r.value) );
|
||||
this.isLoaded = true;
|
||||
}
|
||||
|
||||
//this.resolveConfigConflicts();
|
||||
const version = this.data.schema_version || this.data.__version || defaultSchemaVersion;
|
||||
if( version !== latestSchemaVersion ) {
|
||||
// migrate here
|
||||
// create a copy of the old config
|
||||
fs.copyFileSync( path, path+'-'+version );
|
||||
await this.migrateFrom(version);
|
||||
// validate again to strip all illegal properties from config with latest version
|
||||
this.validate();
|
||||
}
|
||||
}
|
||||
|
||||
resolveConfigConflicts() {
|
||||
// TODO solve this in config schema
|
||||
if( this.data.features && this.data.features.length && this.data.features.indexOf('lightning') !== -1 ) {
|
||||
this.data.bitcoin_prune = false;
|
||||
delete this.data.bitcoin_prune_size;
|
||||
}
|
||||
}
|
||||
|
||||
validate() {
|
||||
const version = this.data.schema_version || this.data.__version;
|
||||
|
||||
if( !version || !this.validators[version] || Object.keys( schemas ).indexOf( version ) == -1 ) {
|
||||
throw "Unknown version in data"
|
||||
}
|
||||
|
||||
// this will assign default values from the schema
|
||||
this.valid = this.validators[version]( this.data );
|
||||
this.validateErrors = this.validators[version].errors;
|
||||
|
||||
}
|
||||
|
||||
async migrateFrom(sourceVersion) {
|
||||
const migrations = this.generateMigrationPathToLatest(sourceVersion)
|
||||
|
||||
if( !migrations ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for( let migration of migrations ) {
|
||||
await migration.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
// migration methods:
|
||||
async migrate_0_1_0_to_0_2_0() {
|
||||
const currentVersion = this.data.schema_version || this.data.__version;
|
||||
if( currentVersion != '0.1.0' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.schema_version = '0.2.0';
|
||||
|
||||
// rewrite specific properties with incompatible content
|
||||
|
||||
// gatekeeper_keys: add stats group to all keys and add a label containing only
|
||||
// the stats group
|
||||
|
||||
const gatekeeper_keys = this.data.gatekeeper_keys;
|
||||
|
||||
for( let i=0; i<gatekeeper_keys.configEntries.length; i++ ) {
|
||||
const apiKey = new ApiKey();
|
||||
apiKey.setFromConfigEntry(gatekeeper_keys.configEntries[i]);
|
||||
apiKey.groups.unshift('stats');
|
||||
gatekeeper_keys.configEntries[i]=apiKey.getConfigEntry();
|
||||
gatekeeper_keys.clientInformation[i]=apiKey.getClientInformation();
|
||||
}
|
||||
|
||||
const apiKeyStatsOnly = new ApiKey('000',['stats']);
|
||||
await apiKeyStatsOnly.randomiseKey();
|
||||
gatekeeper_keys.configEntries.unshift( apiKeyStatsOnly.getConfigEntry() );
|
||||
gatekeeper_keys.clientInformation.unshift( apiKeyStatsOnly.getClientInformation() );
|
||||
|
||||
// remove all empty props to generate proper errors
|
||||
for( let k in this.data ) {
|
||||
if( !this.data.hasOwnProperty(k) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if( this.data[k] === '' ) {
|
||||
delete this.data[k];
|
||||
}
|
||||
|
||||
}
|
||||
// lightning_nodecolor
|
||||
if( !this.data.lightning_nodecolor ) {
|
||||
this.data.lightning_nodecolor =
|
||||
colorsys.hslToHex( { h: (Math.random()*360)<<0, s: 50, l: 50 } ).substr(1);
|
||||
}
|
||||
|
||||
// lightning_nodename
|
||||
if( !this.data.lightning_nodename ) {
|
||||
this.data.lightning_nodename = name.generate();
|
||||
}
|
||||
|
||||
// xpub && use_xpub
|
||||
this.data.use_xpub = !!this.data.xpub;
|
||||
|
||||
}
|
||||
|
||||
async migrate_0_2_0_to_0_2_2() {
|
||||
const currentVersion = this.data.schema_version;
|
||||
if( currentVersion != '0.2.0' ) {
|
||||
return;
|
||||
}
|
||||
this.data.schema_version = '0.2.2';
|
||||
// create identical behaviour to 0.2.0 config version
|
||||
this.data.lightning_announce = !!this.data.lightning_external_ip;
|
||||
this.data.gatekeeper_expose = true;
|
||||
}
|
||||
|
||||
};
|
||||
23
cyphernodeconf_docker/lib/htpasswd.js
Normal file
23
cyphernodeconf_docker/lib/htpasswd.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
module.exports = async ( password ) => {
|
||||
|
||||
if( !password ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
password = password.replace(/'/g, `'\\''`);
|
||||
|
||||
return await new Promise( (resolve) => {
|
||||
exec('htpasswd -bnB admin \''+password+'\' | cut -sd \':\' -f2', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
return resolve(null);
|
||||
}
|
||||
// remove newline at the end
|
||||
resolve(stdout.substr(0,stdout.length-1));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const MAXLENGTH = 30;
|
||||
const MAXLENGTH = 28;
|
||||
|
||||
const ADJECTIVES = [
|
||||
/*a*/ ["Abiding",
|
||||
135
cyphernodeconf_docker/lib/splashScreen.js
Normal file
135
cyphernodeconf_docker/lib/splashScreen.js
Normal file
@@ -0,0 +1,135 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ansi = require( './ansi.js' );
|
||||
const lunicode = require('lunicode');
|
||||
|
||||
const easeOutCubic = (t, b, c, d) => {
|
||||
return c*((t=t/d-1)*t*t+1)+b;
|
||||
};
|
||||
|
||||
lunicode.tools.creepify.options.top = true; // add diacritics on top. Default: true
|
||||
lunicode.tools.creepify.options.middle = true; // add diacritics in the middle. Default: true
|
||||
lunicode.tools.creepify.options.bottom = true; // add diacritics on the bottom. Default: true
|
||||
lunicode.tools.creepify.options.maxHeight = 15; // How many diacritic marks shall we put on top/bottom? Default: 15
|
||||
lunicode.tools.creepify.options.randomization = 100; // 0-100%. maxHeight 100 and randomization 20%: the height goes from 80 to 100. randomization 70%: height goes from 30 to 100. Default: 100
|
||||
|
||||
const fortunes = [
|
||||
'Cause fuck central banking',
|
||||
'Not your keys, not your bitcoin',
|
||||
'Don\'t trust, verify',
|
||||
'Craig Wright is a fraud',
|
||||
'HODL!'
|
||||
];
|
||||
|
||||
module.exports = class SplashScreen {
|
||||
|
||||
constructor( options ) {
|
||||
options = options || {};
|
||||
|
||||
if( !options.frameDir ) {
|
||||
throw "no frame directory to load"
|
||||
}
|
||||
|
||||
this.width = options.width || 82;
|
||||
this.fortuneEnabled = !!options.enableFortune;
|
||||
this.fortuneSpacing = options.fortuneSpacing || 0;
|
||||
this.fortuneChalk = options.fortuneChalk;
|
||||
|
||||
this.loadFramesFromDir( options.frameDir );
|
||||
|
||||
if( this.fortuneEnabled ) {
|
||||
|
||||
let fortune = this.fortune();
|
||||
if( fortune.length > this.width-2 ) {
|
||||
fortune = fortune.substr(0,this.width-2);
|
||||
}
|
||||
fortune = this.center(fortune);
|
||||
|
||||
let fortuneLines = [];
|
||||
|
||||
|
||||
|
||||
|
||||
fortuneLines.push( this.creepify(fortune) )+'\n';
|
||||
|
||||
|
||||
|
||||
|
||||
for( let i=0; i<this.frames.length; i++ ) {
|
||||
for( let j=0; j<fortuneLines.length; j++ ) {
|
||||
this.frames[i] += fortuneLines[j];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
loadFramesFromDir( frameDir ) {
|
||||
this.frames = [];
|
||||
fs.readdirSync(frameDir).forEach((file) => {
|
||||
this.frames.push(fs.readFileSync(path.join(__dirname,'..','splash',file)));
|
||||
});
|
||||
}
|
||||
|
||||
sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
fortune() {
|
||||
return fortunes[ Math.random()*fortunes.length << 0 ];
|
||||
}
|
||||
|
||||
creepify( string ) {
|
||||
if( this.fortuneChalk ) {
|
||||
return this.fortuneChalk(lunicode.tools.creepify.encode( string ));
|
||||
}
|
||||
return lunicode.tools.creepify.encode( string );
|
||||
}
|
||||
|
||||
center( string ) {
|
||||
const offset = ((this.width - string.length)*0.5) << 0;
|
||||
for( let i=0; i<offset; i++ ) {
|
||||
string = ' '+string+' ';
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
async show() {
|
||||
|
||||
const frame0 = this.frames[0];
|
||||
|
||||
const frame0lines = frame0.toString().split('\n');
|
||||
const frame0lineCount = frame0lines.length;
|
||||
const steps = 10;
|
||||
|
||||
await this.sleep(250);
|
||||
|
||||
process.stdout.write(ansi.clear);
|
||||
|
||||
await this.sleep(150);
|
||||
|
||||
for( let i=0; i<=steps; i++ ) {
|
||||
const pos = easeOutCubic( i, 0, frame0lineCount, steps ) | 0;
|
||||
process.stdout.write(ansi.reset);
|
||||
for( let l=frame0lineCount-pos; l<frame0lineCount; l++ ) {
|
||||
process.stdout.write( frame0lines[l]+'\n' );
|
||||
}
|
||||
await this.sleep(33);
|
||||
}
|
||||
|
||||
if( this.frames.length > 1 ) {
|
||||
await this.sleep(400);
|
||||
|
||||
for( let frame of this.frames ) {
|
||||
process.stdout.write(ansi.reset);
|
||||
process.stdout.write(frame.toString());
|
||||
await this.sleep(33);
|
||||
}
|
||||
}
|
||||
|
||||
await this.sleep(400);
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
|
||||
};
|
||||
5508
cyphernodeconf_docker/package-lock.json
generated
Normal file
5508
cyphernodeconf_docker/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
cyphernodeconf_docker/package.json
Normal file
37
cyphernodeconf_docker/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "cyphernodeconf",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"homepage": "",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"coverageHtml": "jest --coverage --coverageReporters html",
|
||||
"lint": "./node_modules/.bin/eslint lib/**/*.js",
|
||||
"lintfix": "./node_modules/.bin/eslint lib/**/**.js --fix"
|
||||
},
|
||||
"author": "jash <jash@schulterklopfer-productions.de>",
|
||||
"main": "lib/index.js",
|
||||
"keywords": [
|
||||
"cyphernode"
|
||||
],
|
||||
"dependencies": {
|
||||
"@rauschma/stringio": "^1.4.0",
|
||||
"ajv": "^6.10.0",
|
||||
"chalk": "^2.4.2",
|
||||
"coinstring": "^2.3.0",
|
||||
"colorsys": "^1.0.22",
|
||||
"ejs": "^2.6.1",
|
||||
"inquirer": "^6.3.1",
|
||||
"lunicode": "^2.0.1",
|
||||
"parse5": "^5.1.0",
|
||||
"tmp": "^0.1.0",
|
||||
"validator": "^10.11.0",
|
||||
"wrap-ansi": "^5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^5.16.0",
|
||||
"jest": "^24.8.0"
|
||||
},
|
||||
"repository": "git@github.com:schulterklopfer/cyphernodeconf.git",
|
||||
"license": "MIT"
|
||||
}
|
||||
@@ -20,27 +20,30 @@ module.exports = {
|
||||
// input, confirm, list, rawlist, expand, checkbox, password, editor
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: prefix()+'What features do you want to add to your cyphernode?'+utils._getHelp('features'),
|
||||
choices: utils._featureChoices()
|
||||
message: prefix()+'What features do you want to add to your cyphernode?'+utils.getHelp('features'),
|
||||
choices: utils.featureChoices()
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'net',
|
||||
default: utils._getDefault( 'net' ),
|
||||
message: prefix()+'What net do you want to run on?'+utils._getHelp('net'),
|
||||
default: utils.getDefault( 'net' ),
|
||||
message: prefix()+'What net do you want to run on?'+utils.getHelp('net'),
|
||||
choices: [{
|
||||
name: "Testnet",
|
||||
value: "testnet"
|
||||
},{
|
||||
name: "Mainnet",
|
||||
value: "mainnet"
|
||||
},{
|
||||
name: "RegTest",
|
||||
value: "regtest"
|
||||
}]
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'run_as_different_user',
|
||||
default: utils._getDefault( 'run_as_different_user' ),
|
||||
message: prefix()+'Run as different user?'+utils._getHelp('run_as_different_user')
|
||||
default: utils.getDefault( 'run_as_different_user' ),
|
||||
message: prefix()+'Run as different user?'+utils.getHelp('run_as_different_user')
|
||||
},
|
||||
{
|
||||
when: function( props ) {
|
||||
@@ -48,16 +51,16 @@ module.exports = {
|
||||
},
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
default: utils._getDefault( 'username' ),
|
||||
message: prefix()+'What username will cyphernode run under?'+utils._getHelp('username'),
|
||||
filter: utils._trimFilter,
|
||||
validate: utils._usernameValidator
|
||||
default: utils.getDefault( 'username' ),
|
||||
message: prefix()+'What username will cyphernode run under?'+utils.getHelp('username'),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.usernameValidator
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'use_xpub',
|
||||
default: utils._getDefault( 'use_xpub' )||false,
|
||||
message: prefix()+'Use a default xpub key to watch or generate adresses?'+utils._getHelp('use_xpub'),
|
||||
default: utils.getDefault( 'use_xpub' )||false,
|
||||
message: prefix()+'Use a default xpub key to watch or generate adresses?'+utils.getHelp('use_xpub'),
|
||||
},
|
||||
{
|
||||
when: function( props ) {
|
||||
@@ -65,10 +68,10 @@ module.exports = {
|
||||
},
|
||||
type: 'input',
|
||||
name: 'xpub',
|
||||
default: utils._getDefault( 'xpub' ),
|
||||
message: prefix()+'What is your default xpub key?'+utils._getHelp('xpub'),
|
||||
filter: utils._trimFilter,
|
||||
validate: utils._xkeyValidator
|
||||
default: utils.getDefault( 'xpub' ),
|
||||
message: prefix()+'What is your default xpub key?'+utils.getHelp('xpub'),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.xkeyValidator
|
||||
},
|
||||
{
|
||||
when: function( props ) {
|
||||
@@ -76,13 +79,13 @@ module.exports = {
|
||||
},
|
||||
type: 'input',
|
||||
name: 'derivation_path',
|
||||
default: utils._getDefault( 'derivation_path' ),
|
||||
message: prefix()+'What is your default derivation path?'+utils._getHelp('derivation_path'),
|
||||
filter: utils._trimFilter,
|
||||
validate: utils._derivationPathValidator
|
||||
default: utils.getDefault( 'derivation_path' ),
|
||||
message: prefix()+'What is your default derivation path?'+utils.getHelp('derivation_path'),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.derivationPathValidator
|
||||
}];
|
||||
},
|
||||
templates: function( props ) {
|
||||
return [];
|
||||
return ['info.json'];
|
||||
}
|
||||
};
|
||||
95
cyphernodeconf_docker/prompters/010_gatekeeper.js
Normal file
95
cyphernodeconf_docker/prompters/010_gatekeeper.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const chalk = require('chalk');
|
||||
|
||||
const name = 'gatekeeper';
|
||||
|
||||
const capitalise = function( txt ) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
};
|
||||
|
||||
const prefix = function() {
|
||||
return chalk.green(capitalise(name)+': ');
|
||||
};
|
||||
|
||||
const hasAuthKeys = function( props ) {
|
||||
return props &&
|
||||
props.gatekeeper_keys &&
|
||||
props.gatekeeper_keys.configEntries &&
|
||||
props.gatekeeper_keys.configEntries.length > 0;
|
||||
}
|
||||
|
||||
const hasCert = function( props ) {
|
||||
return props &&
|
||||
props.gatekeeper_sslkey &&
|
||||
props.gatekeeper_sslcert
|
||||
}
|
||||
|
||||
let password = '';
|
||||
|
||||
module.exports = {
|
||||
name: function() {
|
||||
return name;
|
||||
},
|
||||
prompts: function( utils ) {
|
||||
// TODO: delete clientKeys archive when password chnages
|
||||
return [{
|
||||
type: 'password',
|
||||
name: 'gatekeeper_clientkeyspassword',
|
||||
default: utils.getDefault( 'gatekeeper_clientkeyspassword' ),
|
||||
message: prefix()+'Enter a password to protect your client keys with'+utils.getHelp('gatekeeper_clientkeyspassword'),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.notEmptyValidator
|
||||
},
|
||||
{
|
||||
when: function( props ) {
|
||||
// hacky hack
|
||||
password = props.gatekeeper_clientkeyspassword;
|
||||
return true;
|
||||
},
|
||||
type: 'password',
|
||||
name: 'gatekeeper_clientkeyspassword_c',
|
||||
default: utils.getDefault( 'gatekeeper_clientkeyspassword_c' ),
|
||||
message: prefix()+'Confirm your client keys password.'+utils.getHelp('gatekeeper_clientkeyspassword_c'),
|
||||
filter: utils.trimFilter,
|
||||
validate: function( input ) {
|
||||
if(input !== password) {
|
||||
throw new Error( 'Client keys passwords do not match' );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'gatekeeper_port',
|
||||
default: utils.getDefault( 'gatekeeper_port' ),
|
||||
message: prefix()+'The port gatekeeper will listen on for requests'+utils.getHelp('gatekeeper_port'),
|
||||
filter: utils.trimFilter,
|
||||
validate: function( port ) {
|
||||
return utils.notEmptyValidator( port ) && !isNaN( parseInt(port) )
|
||||
}
|
||||
},
|
||||
{
|
||||
when: function() { return hasAuthKeys( utils.props ); },
|
||||
type: 'confirm',
|
||||
name: 'gatekeeper_recreatekeys',
|
||||
default: false,
|
||||
message: prefix()+'Recreate gatekeeper keys?'+utils.getHelp('gatekeeper_recreatekeys')
|
||||
},
|
||||
{
|
||||
when: function() { return hasCert( utils.props ); },
|
||||
type: 'confirm',
|
||||
name: 'gatekeeper_recreatecert',
|
||||
default: false,
|
||||
message: prefix()+'Recreate gatekeeper certificate?'+utils.getHelp('gatekeeper_recreatecert')
|
||||
},
|
||||
{
|
||||
when: function(props) { return !hasCert( utils.props ) || props.gatekeeper_recreatecert },
|
||||
type: 'input',
|
||||
name: 'gatekeeper_cns',
|
||||
default: utils.getDefault( 'gatekeeper_cns' ),
|
||||
message: prefix()+'Gatekeeper cert CNS (ips, domains, wildcard domains seperated by comma)?'+utils.getHelp('gatekeeper_cns')
|
||||
}];
|
||||
},
|
||||
templates: function( props ) {
|
||||
return [ 'keys.properties', 'api.properties', 'cert.pem', 'key.pem', 'default.conf' ];
|
||||
}
|
||||
};
|
||||
44
cyphernodeconf_docker/prompters/030_traefik.js
Normal file
44
cyphernodeconf_docker/prompters/030_traefik.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const chalk = require('chalk');
|
||||
|
||||
const name = 'traefik';
|
||||
|
||||
const capitalise = function( txt ) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
};
|
||||
|
||||
const prefix = function() {
|
||||
return chalk.green(capitalise(name)+': ');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: function() {
|
||||
return name;
|
||||
},
|
||||
prompts: function( utils ) {
|
||||
return [
|
||||
{
|
||||
type: 'input',
|
||||
name: 'traefik_http_port',
|
||||
default: utils.getDefault( 'traefik_http_port' ),
|
||||
message: prefix()+'The HTTP port your apps will be accessible to the internet on.'+utils.getHelp('traefik_http_port'),
|
||||
filter: utils.trimFilter,
|
||||
validate: function( port ) {
|
||||
return utils.notEmptyValidator( port ) && !isNaN( parseInt(port) )
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'traefik_https_port',
|
||||
default: utils.getDefault( 'traefik_https_port' ),
|
||||
message: prefix()+'The HTTPS port your apps will be accessible to the internet on.'+utils.getHelp('traefik_https_port'),
|
||||
filter: utils.trimFilter,
|
||||
validate: function( port ) {
|
||||
return utils.notEmptyValidator( port ) && !isNaN( parseInt(port) )
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
templates: function( props ) {
|
||||
return [ 'acme.json', 'traefik.toml', 'htpasswd' ];
|
||||
}
|
||||
};
|
||||
@@ -17,7 +17,7 @@ const featureCondition = function(props) {
|
||||
|
||||
const templates = {
|
||||
'lnd': [ path.join('lnd','lnd.conf') ],
|
||||
'c-lightning': [ path.join('c-lightning','config'), path.join('c-lightning','bitcoin.conf') ]
|
||||
'c-lightning': [ path.join('c-lightning','config') ]
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@@ -31,8 +31,8 @@ module.exports = {
|
||||
when: featureCondition,
|
||||
type: 'list',
|
||||
name: 'lightning_implementation',
|
||||
default: utils._getDefault( 'lightning_implementation' ),
|
||||
message: prefix()+'What lightning implementation do you want to use?'+utils._getHelp('lightning_implementation'),
|
||||
default: utils.getDefault( 'lightning_implementation' ),
|
||||
message: prefix()+'What lightning implementation do you want to use?'+utils.getHelp('lightning_implementation'),
|
||||
choices: [
|
||||
{
|
||||
name: 'C-lightning',
|
||||
@@ -46,36 +46,52 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
*/
|
||||
{
|
||||
when: featureCondition,
|
||||
type: 'confirm',
|
||||
name: 'lightning_announce',
|
||||
default: utils.getDefault( 'lightning_announce' ),
|
||||
message: prefix()+'Do you want to announce your lightning node?'+utils.getHelp('lightning_announce'),
|
||||
},
|
||||
{
|
||||
when: (props) => { return featureCondition(props) && props.lightning_announce },
|
||||
type: 'input',
|
||||
name: 'lightning_external_ip',
|
||||
default: utils.getDefault( 'lightning_external_ip' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.ipOrFQDNValidator,
|
||||
message: prefix()+'What external ip does your lightning node have?'+utils.getHelp('lightning_external_ip'),
|
||||
},
|
||||
{
|
||||
when: featureCondition,
|
||||
type: 'input',
|
||||
name: 'lightning_nodename',
|
||||
default: utils._getDefault( 'lightning_nodename' ),
|
||||
filter: utils._trimFilter,
|
||||
default: utils.getDefault( 'lightning_nodename' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: (input)=>{
|
||||
if( !input.trim() ) {
|
||||
return true;
|
||||
}
|
||||
return utils._lightningNodeNameValidator(input);
|
||||
return utils.lightningNodeNameValidator(input);
|
||||
},
|
||||
message: prefix()+'What name has your lightning node?'+utils._getHelp('lightning_nodename'),
|
||||
message: prefix()+'What name has your lightning node?'+utils.getHelp('lightning_nodename'),
|
||||
},
|
||||
{
|
||||
when: featureCondition,
|
||||
type: 'input',
|
||||
name: 'lightning_nodecolor',
|
||||
default: utils._getDefault( 'lightning_nodecolor' ),
|
||||
filter: utils._trimFilter,
|
||||
default: utils.getDefault( 'lightning_nodecolor' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: (input)=>{
|
||||
if( !input.trim() ) {
|
||||
return true;
|
||||
}
|
||||
return utils._colorValidator(input);
|
||||
return utils.colorValidator(input);
|
||||
},
|
||||
message: prefix()+'What color has your lightning node?'+utils._getHelp('lightning_nodecolor'),
|
||||
message: prefix()+'What color has your lightning node?'+utils.getHelp('lightning_nodecolor'),
|
||||
}];
|
||||
},
|
||||
templates: function( props ) {
|
||||
return templates[props.lightning_implementation]
|
||||
return featureCondition(props)?templates[props.lightning_implementation]:[];
|
||||
}
|
||||
};
|
||||
@@ -23,7 +23,7 @@ const bitcoinInternalAndPrune = function(props) {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: function() {
|
||||
name: function() {
|
||||
return name;
|
||||
},
|
||||
prompts: function( utils ) {
|
||||
@@ -31,16 +31,12 @@ module.exports = {
|
||||
{
|
||||
type: 'list',
|
||||
name: 'bitcoin_mode',
|
||||
default: utils._getDefault( 'bitcoin_mode' ),
|
||||
message: prefix()+'Where is your bitcoin full node running?'+utils._getHelp('bitcoin_mode'),
|
||||
default: utils.getDefault( 'bitcoin_mode' ),
|
||||
message: prefix()+'Cyphernode will manage your bitcoin full node.'+utils.getHelp('bitcoin_mode'),
|
||||
choices: [
|
||||
{
|
||||
name: 'Nowhere! I want cyphernode to run one.',
|
||||
name: 'Ok. That is awesome',
|
||||
value: 'internal'
|
||||
},
|
||||
{
|
||||
name: 'I have a full node running.',
|
||||
value: 'external'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -48,38 +44,42 @@ module.exports = {
|
||||
when: bitcoinExternal,
|
||||
type: 'input',
|
||||
name: 'bitcoin_node_ip',
|
||||
default: utils._getDefault( 'bitcoin_node_ip' ),
|
||||
filter: utils._trimFilter,
|
||||
validate: utils._ipOrFQDNValidator,
|
||||
message: prefix()+'What is your full node ip address?'+utils._getHelp('bitcoin_node_ip'),
|
||||
default: utils.getDefault( 'bitcoin_node_ip' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.ipOrFQDNValidator,
|
||||
message: prefix()+'What is your full node ip address?'+utils.getHelp('bitcoin_node_ip'),
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'bitcoin_rpcuser',
|
||||
default: utils._getDefault( 'bitcoin_rpcuser' ),
|
||||
message: prefix()+'Name of bitcoin rpc user?'+utils._getHelp('bitcoin_rpcuser'),
|
||||
filter: utils._trimFilter,
|
||||
default: utils.getDefault( 'bitcoin_rpcuser' ),
|
||||
message: prefix()+'Name of bitcoin rpc user?'+utils.getHelp('bitcoin_rpcuser'),
|
||||
filter: utils.trimFilter,
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'bitcoin_rpcpassword',
|
||||
default: utils._getDefault( 'bitcoin_rpcpassword' ),
|
||||
message: prefix()+'Password of bitcoin rpc user?'+utils._getHelp('bitcoin_rpcpassword'),
|
||||
filter: utils._trimFilter,
|
||||
default: utils.getDefault( 'bitcoin_rpcpassword' ),
|
||||
message: prefix()+'Password of bitcoin rpc user?'+utils.getHelp('bitcoin_rpcpassword'),
|
||||
filter: utils.trimFilter,
|
||||
},
|
||||
{
|
||||
when: bitcoinInternal,
|
||||
when: function(props) {
|
||||
return bitcoinInternal( props ) && props.features.indexOf('lightning') === -1;
|
||||
},
|
||||
type: 'confirm',
|
||||
name: 'bitcoin_prune',
|
||||
default: utils._getDefault( 'bitcoin_prune' ),
|
||||
message: prefix()+'Run bitcoin node in prune mode?'+utils._getHelp('bitcoin_prune'),
|
||||
default: utils.getDefault( 'bitcoin_prune' ),
|
||||
message: prefix()+'Run bitcoin node in prune mode?'+utils.getHelp('bitcoin_prune'),
|
||||
},
|
||||
{
|
||||
when: bitcoinInternalAndPrune,
|
||||
when: function(props) {
|
||||
return bitcoinInternalAndPrune( props ) && props.features.indexOf('lightning') === -1;
|
||||
},
|
||||
type: 'input',
|
||||
name: 'bitcoin_prune_size',
|
||||
default: utils._getDefault( 'bitcoin_prune_size' ),
|
||||
message: prefix()+'What is the maximum size of your blockchain data in megabytes?'+utils._getHelp('bitcoin_prune_size'),
|
||||
default: utils.getDefault( 'bitcoin_prune_size' ),
|
||||
message: prefix()+'What is the maximum size of your blockchain data in megabytes?'+utils.getHelp('bitcoin_prune_size'),
|
||||
validate: function( input ) {
|
||||
if( ! /^\d+$/.test(input) ) {
|
||||
throw new Error( "Not a number");
|
||||
@@ -94,16 +94,16 @@ module.exports = {
|
||||
when: bitcoinInternal,
|
||||
type: 'input',
|
||||
name: 'bitcoin_uacomment',
|
||||
default: utils._getDefault( 'bitcoin_uacomment' ),
|
||||
message: prefix()+'Any UA comment?'+utils._getHelp('bitcoin_uacomment'),
|
||||
filter: utils._trimFilter,
|
||||
validate: (input)=> {return utils._optional(input,utils._UACommentValidator) }
|
||||
default: utils.getDefault( 'bitcoin_uacomment' ),
|
||||
message: prefix()+'Any UA comment?'+utils.getHelp('bitcoin_uacomment'),
|
||||
filter: utils.trimFilter,
|
||||
validate: (input)=> {return utils.optional(input,utils.UACommentValidator) }
|
||||
}];
|
||||
},
|
||||
env: function( props ) {
|
||||
return 'VAR0=VALUE0\nVAR1=VALUE1'
|
||||
},
|
||||
templates: function( props ) {
|
||||
return ['bitcoin.conf']
|
||||
return ['bitcoin.conf', 'bitcoin-client.conf']
|
||||
}
|
||||
};
|
||||
309
cyphernodeconf_docker/prompters/999_installer.js
Normal file
309
cyphernodeconf_docker/prompters/999_installer.js
Normal file
@@ -0,0 +1,309 @@
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
const name = 'installer';
|
||||
|
||||
const capitalise = function( txt ) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
};
|
||||
|
||||
const prefix = function() {
|
||||
return chalk.green(capitalise(name)+': ');
|
||||
};
|
||||
|
||||
const installerDocker = function(props) {
|
||||
return props.installer_mode === 'docker'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: function() {
|
||||
return name;
|
||||
},
|
||||
prompts: function( utils ) {
|
||||
return [{
|
||||
type: 'list',
|
||||
name: 'installer_mode',
|
||||
default: utils.getDefault( 'installer_mode' ),
|
||||
message: prefix()+chalk.red('Where do you want to install cyphernode?')+utils.getHelp('installer_mode'),
|
||||
choices: [{
|
||||
name: "Docker",
|
||||
value: "docker"
|
||||
}]
|
||||
},
|
||||
{
|
||||
when: installerDocker,
|
||||
type: 'list',
|
||||
name: 'traefik_datapath',
|
||||
default: utils.getDefault( 'traefik_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/traefik",
|
||||
value: utils.setupDir()+"/cyphernode/traefik"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/traefik",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/traefik"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/traefik",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/traefik"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/traefik",
|
||||
value: utils.defaultDataDirBase()+"/traefik"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your traefik data?'+utils.getHelp('traefik_datapath'),
|
||||
},
|
||||
{
|
||||
when: (props)=>{ return installerDocker(props) && (props.traefik_datapath === '_custom') },
|
||||
type: 'input',
|
||||
name: 'traefik_datapath_custom',
|
||||
default: utils.getDefault( 'traefik_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Custom path for traefik data?'+utils.getHelp('traefik_datapath_custom'),
|
||||
},
|
||||
{
|
||||
when: installerDocker,
|
||||
type: 'list',
|
||||
name: 'gatekeeper_datapath',
|
||||
default: utils.getDefault( 'gatekeeper_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/gatekeeper",
|
||||
value: utils.setupDir()+"/cyphernode/gatekeeper"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/gatekeeper",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/gatekeeper"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/gatekeeper",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/gatekeeper"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/gatekeeper",
|
||||
value: utils.defaultDataDirBase()+"/gatekeeper"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your gatekeeper data?'+utils.getHelp('gatekeeper_datapath'),
|
||||
},
|
||||
{
|
||||
when: (props)=>{ return installerDocker(props) && (props.gatekeeper_datapath === '_custom') },
|
||||
type: 'input',
|
||||
name: 'gatekeeper_datapath_custom',
|
||||
default: utils.getDefault( 'gatekeeper_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Custom path for gatekeeper data?'+utils.getHelp('gatekeeper_datapath_custom'),
|
||||
},
|
||||
{
|
||||
when: installerDocker,
|
||||
type: 'list',
|
||||
name: 'proxy_datapath',
|
||||
default: utils.getDefault( 'proxy_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/proxy",
|
||||
value: utils.setupDir()+"/cyphernode/proxy"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/proxy",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/proxy"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/proxy",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/proxy"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/proxy",
|
||||
value: utils.defaultDataDirBase()+"/proxy"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your proxy data?'+utils.getHelp('proxy_datapath'),
|
||||
},
|
||||
{
|
||||
when: (props)=>{ return installerDocker(props) && (props.proxy_datapath === '_custom') },
|
||||
type: 'input',
|
||||
name: 'proxy_datapath_custom',
|
||||
default: utils.getDefault( 'proxy_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Custom path for your proxy data?'+utils.getHelp('proxy_datapath_custom'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' },
|
||||
type: 'list',
|
||||
name: 'bitcoin_datapath',
|
||||
default: utils.getDefault( 'bitcoin_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/bitcoin",
|
||||
value: utils.setupDir()+"/cyphernode/bitcoin"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/bitcoin",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/bitcoin"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/bitcoin",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/bitcoin"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/bitcoin",
|
||||
value: utils.defaultDataDirBase()+"/bitcoin"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your bitcoin full node data?'+utils.getHelp('bitcoin_datapath'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' && props.bitcoin_datapath === '_custom' },
|
||||
type: 'input',
|
||||
name: 'bitcoin_datapath_custom',
|
||||
default: utils.getDefault( 'bitcoin_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Custom path for your bitcoin full node data?'+utils.getHelp('bitcoin_datapath_custom'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 },
|
||||
type: 'list',
|
||||
name: 'lightning_datapath',
|
||||
default: utils.getDefault( 'lightning_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/lightning",
|
||||
value: utils.setupDir()+"/cyphernode/lightning"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/lightning",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/lightning"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/lightning",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/lightning"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/lightning",
|
||||
value: utils.defaultDataDirBase()+"/lightning"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your lightning node data?'+utils.getHelp('lightning_datapath'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 && props.lightning_datapath === '_custom'},
|
||||
type: 'input',
|
||||
name: 'lightning_datapath_custom',
|
||||
default: utils.getDefault( 'lightning_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Custom path for your lightning node data?'+utils.getHelp('lightning_datapath_custom'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 },
|
||||
type: 'list',
|
||||
name: 'otsclient_datapath',
|
||||
default: utils.getDefault( 'otsclient_datapath' ),
|
||||
choices: [
|
||||
{
|
||||
name: utils.setupDir()+"/cyphernode/otsclient",
|
||||
value: utils.setupDir()+"/cyphernode/otsclient"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/cyphernode/otsclient",
|
||||
value: utils.defaultDataDirBase()+"/cyphernode/otsclient"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/.cyphernode/otsclient",
|
||||
value: utils.defaultDataDirBase()+"/.cyphernode/otsclient"
|
||||
},
|
||||
{
|
||||
name: utils.defaultDataDirBase()+"/otsclient",
|
||||
value: utils.defaultDataDirBase()+"/otsclient"
|
||||
},
|
||||
{
|
||||
name: "Custom path",
|
||||
value: "_custom"
|
||||
}
|
||||
],
|
||||
message: prefix()+'Where do you want to store your OTS data?'+utils.getHelp('otsclient_datapath'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 && props.otsclient_datapath === '_custom' },
|
||||
type: 'input',
|
||||
name: 'otsclient_datapath_custom',
|
||||
default: utils.getDefault( 'otsclient_datapath_custom' ),
|
||||
filter: utils.trimFilter,
|
||||
validate: utils.pathValidator,
|
||||
message: prefix()+'Where is your otsclient data?'+utils.getHelp('otsclient_datapath_custom'),
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'gatekeeper_expose',
|
||||
default: utils.getDefault( 'gatekeeper_expose' ),
|
||||
message: prefix()+'Expose gatekeeper outside of the docker network?'+utils.getHelp('gatekeeper_expose'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' },
|
||||
type: 'confirm',
|
||||
name: 'bitcoin_expose',
|
||||
default: utils.getDefault( 'bitcoin_expose' ),
|
||||
message: prefix()+'Expose bitcoin full node outside of the docker network?'+utils.getHelp('bitcoin_expose'),
|
||||
},
|
||||
{
|
||||
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 },
|
||||
type: 'confirm',
|
||||
name: 'lightning_expose',
|
||||
default: utils.getDefault( 'lightning_expose' ),
|
||||
message: prefix()+'Expose lightning node outside of the docker network?'+utils.getHelp('lightning_expose'),
|
||||
},
|
||||
{
|
||||
when: installerDocker,
|
||||
type: 'list',
|
||||
name: 'docker_mode',
|
||||
default: utils.getDefault( 'docker_mode' ),
|
||||
message: prefix()+'What docker mode: docker swarm or docker-compose?'+utils.getHelp('docker_mode'),
|
||||
choices: [{
|
||||
name: "docker swarm",
|
||||
value: "swarm"
|
||||
},
|
||||
{
|
||||
name: "docker-compose",
|
||||
value: "compose"
|
||||
}]
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'installer_cleanup',
|
||||
default: utils.getDefault( 'installer_cleanup' ),
|
||||
message: prefix()+'Cleanup installer after installation?'+utils.getHelp('installer_cleanup'),
|
||||
}];
|
||||
},
|
||||
templates: function( props ) {
|
||||
if( props.installer_mode === 'docker' ) {
|
||||
return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh', 'testdeployment.sh', path.join('docker', 'docker-compose.yaml')];
|
||||
}
|
||||
return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh', 'testdeployment.sh'];
|
||||
}
|
||||
};
|
||||
42
cyphernodeconf_docker/run.sh
Executable file
42
cyphernodeconf_docker/run.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
export SETUP_DIR=$(pwd)/../dist
|
||||
export DEFAULT_USER=$USER
|
||||
export DEFAULT_CERT_HOSTNAME=disk0book.local
|
||||
export PROXYCRON_VERSION=v0.2.4
|
||||
export PYCOIN_VERSION=v0.2.4
|
||||
export SETUP_VERSION=v0.2.4
|
||||
export BITCOIN_VERSION=v0.18.0
|
||||
export LIGHTNING_VERSION=v0.7.1
|
||||
export DEFAULT_DATADIR_BASE=$HOME
|
||||
export GATEKEEPER_VERSION=v0.2.4
|
||||
export PROXY_VERSION=v0.2.4
|
||||
export OTSCLIENT_VERSION=v0.2.4
|
||||
export NOTIFIER_VERSION=v0.2.4
|
||||
export EDITOR=/usr/bin/nano
|
||||
|
||||
user=$(id -u):$(id -g)
|
||||
|
||||
if [ "${MODE}" = 'docker' ]; then
|
||||
docker build . -t cyphernodeconf:local
|
||||
docker run -v $(pwd)/testinst:/data \
|
||||
-e DEFAULT_USER=jash \
|
||||
-e DEFAULT_DATADIR_BASE=$HOME \
|
||||
-e SETUP_DIR=$SETUP_DIR \
|
||||
-e DEFAULT_CERT_HOSTNAME=$(hostname) \
|
||||
-e GATEKEEPER_VERSION=$GATEKEEPER_VERSION \
|
||||
-e PROXY_VERSION=$PROXY_VERSION \
|
||||
-e NOTIFIER_VERSION=$NOTIFIER_VERSION \
|
||||
-e PROXYCRON_VERSION=$PROXYCRON_VERSION \
|
||||
-e OTSCLIENT_VERSION=$OTSCLIENT_VERSION \
|
||||
-e PYCOIN_VERSION=$PYCOIN_VERSION \
|
||||
-e BITCOIN_VERSION=$BITCOIN_VERSION \
|
||||
-e LIGHTNING_VERSION=$LIGHTNING_VERSION \
|
||||
-e SETUP_VERSION=$SETUP_VERSION \
|
||||
-e DEFAULT_USER=$DEFAULT_USER \
|
||||
--log-driver=none \
|
||||
--network none \
|
||||
--rm -it cyphernodeconf:local $user node index.js $@
|
||||
else
|
||||
/usr/local/bin/node index.js $@
|
||||
fi
|
||||
466
cyphernodeconf_docker/schema/config-v0.1.0.json
Normal file
466
cyphernodeconf_docker/schema/config-v0.1.0.json
Normal file
@@ -0,0 +1,466 @@
|
||||
{
|
||||
"definitions": {},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://cyphernode.io/config-v0.1.0.json",
|
||||
"type": "object",
|
||||
"title": "Cyphernode config file structure v0.1.0",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"__version",
|
||||
"features",
|
||||
"net",
|
||||
"xpub",
|
||||
"derivation_path",
|
||||
"installer_mode",
|
||||
"run_as_different_user",
|
||||
"username",
|
||||
"docker_mode",
|
||||
"bitcoin_rpcuser",
|
||||
"bitcoin_rpcpassword",
|
||||
"bitcoin_prune",
|
||||
"bitcoin_datapath",
|
||||
"bitcoin_node_ip",
|
||||
"bitcoin_mode",
|
||||
"bitcoin_expose",
|
||||
"gatekeeper_apiproperties",
|
||||
"gatekeeper_keys",
|
||||
"gatekeeper_sslcert",
|
||||
"gatekeeper_sslkey",
|
||||
"gatekeeper_cns",
|
||||
"gatekeeper_clientkeyspassword",
|
||||
"gatekeeper_clientkeyspassword_c",
|
||||
"gatekeeper_statuspw",
|
||||
"gatekeeper_datapath",
|
||||
"lightning_expose",
|
||||
"lightning_external_ip",
|
||||
"lightning_implementation",
|
||||
"lightning_datapath",
|
||||
"lightning_nodename",
|
||||
"lightning_nodecolor",
|
||||
"otsclient_datapath",
|
||||
"proxy_datapath"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"run_as_different_user": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"username"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"bitcoin_prune": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"bitcoin_prune_size"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"__version": {
|
||||
"type": "string",
|
||||
"enum": ["0.1.0"],
|
||||
"default": "0.1.0",
|
||||
"examples": ["0.1.0"]
|
||||
},
|
||||
"features": {
|
||||
"$id": "#/properties/features",
|
||||
"type": "array",
|
||||
"title": "The optional features of this cyphernode",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$id": "#/properties/features/items",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"title": "The feature",
|
||||
"examples": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
]
|
||||
}
|
||||
},
|
||||
"net": {
|
||||
"$id": "#/properties/net",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"testnet",
|
||||
"mainnet"
|
||||
],
|
||||
"title": "The net cyphernode is running on",
|
||||
"default": "testnet",
|
||||
"examples": [
|
||||
"testnet"
|
||||
]
|
||||
},
|
||||
"xpub": {
|
||||
"$id": "#/properties/xpub",
|
||||
"type": "string",
|
||||
"title": "Default xpub to derive addresses from",
|
||||
"pattern": "^(\\w+)$"
|
||||
},
|
||||
"derivation_path": {
|
||||
"$id": "#/properties/derivation_path",
|
||||
"type": "string",
|
||||
"title": "Default derivation path",
|
||||
"default": "0/n",
|
||||
"examples": [
|
||||
"0/n"
|
||||
]
|
||||
},
|
||||
"installer_mode": {
|
||||
"$id": "#/properties/installer_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"docker"
|
||||
],
|
||||
"title": "Install mode",
|
||||
"default": "docker",
|
||||
"examples": [
|
||||
"docker"
|
||||
]
|
||||
},
|
||||
"run_as_different_user": {
|
||||
"$id": "#/properties/run_as_different_user",
|
||||
"type": "boolean",
|
||||
"title": "Run as different user",
|
||||
"default": true,
|
||||
"examples": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"username": {
|
||||
"$id": "#/properties/username",
|
||||
"type": "string",
|
||||
"title": "Username to run under",
|
||||
"default": "cyphernode",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"docker_mode": {
|
||||
"$id": "#/properties/docker_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"swarm",
|
||||
"compose"
|
||||
],
|
||||
"title": "How to run the containers",
|
||||
"default": "compose",
|
||||
"examples": [
|
||||
"compose"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcuser": {
|
||||
"$id": "#/properties/bitcoin_rpcuser",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc user",
|
||||
"default": "bitcoin",
|
||||
"examples": [
|
||||
"bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcpassword": {
|
||||
"$id": "#/properties/bitcoin_rpcpassword",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc password",
|
||||
"default": "CHANGEME",
|
||||
"examples": [
|
||||
"CHANGEME"
|
||||
]
|
||||
},
|
||||
"bitcoin_uacomment": {
|
||||
"$id": "#/properties/bitcoin_uacomment",
|
||||
"type": "string",
|
||||
"title": "Bitcoin user agent comment",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune": {
|
||||
"$id": "#/properties/bitcoin_prune",
|
||||
"type": "boolean",
|
||||
"title": "Bitcoin prune",
|
||||
"default": false,
|
||||
"examples": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune_size": {
|
||||
"$id": "#/properties/bitcoin_prune_size",
|
||||
"type": "integer",
|
||||
"title": "Bitcoin prune size",
|
||||
"default": 550,
|
||||
"examples": [
|
||||
550
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath": {
|
||||
"$id": "#/properties/bitcoin_datapath",
|
||||
"type": "string",
|
||||
"title": "Bitcoin datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath_custom": {
|
||||
"$id": "#/properties/bitcoin_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Bitcoin custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"lightning_datapath": {
|
||||
"$id": "#/properties/lightning_datapath",
|
||||
"type": "string",
|
||||
"title": "Lightning datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"lightning_datapath_custom": {
|
||||
"$id": "#/properties/lightning_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Lightning custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"proxy_datapath": {
|
||||
"$id": "#/properties/proxy_datapath",
|
||||
"type": "string",
|
||||
"title": "Proxy datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"proxy_datapath_custom": {
|
||||
"$id": "#/properties/proxy_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Proxy custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath": {
|
||||
"$id": "#/properties/otsclient_datapath",
|
||||
"type": "string",
|
||||
"title": "OTS Client datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath_custom": {
|
||||
"$id": "#/properties/otsclient_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "OTS Client custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"bitcoin_node_ip": {
|
||||
"$id": "#/properties/bitcoin_node_ip",
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"title": "Bitcoin node ip",
|
||||
"examples": [
|
||||
"123.123.123.123"
|
||||
]
|
||||
},
|
||||
"bitcoin_mode": {
|
||||
"$id": "#/properties/bitcoin_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal"
|
||||
],
|
||||
"title": "Bitcoin mode",
|
||||
"examples": [
|
||||
"internal"
|
||||
]
|
||||
},
|
||||
"bitcoin_expose": {
|
||||
"$id": "#/properties/bitcoin_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose bitcoin node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"lightning_expose": {
|
||||
"$id": "#/properties/lightning_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose lightning node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"lightning_external_ip": {
|
||||
"$id": "#/properties/lightning_external_ip",
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"title": "Lightning node reachable ip",
|
||||
"examples": [
|
||||
"123.123.123.123"
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath": {
|
||||
"$id": "#/properties/gatekeeper_datapath",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath_custom": {
|
||||
"$id": "#/properties/gatekeeper_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_apiproperties": {
|
||||
"$id": "#/properties/gatekeeper_apiproperties",
|
||||
"type": "string",
|
||||
"title": "API properties",
|
||||
"examples": [
|
||||
"# Stats can:\naction_getblockchaininfo=stats\n\n# Watcher can:\naction_watch=watcher\naction_unwatch=watcher\naction_watchxpub=watcher\naction_unwatchxpubbyxpub=watcher\naction_unwatchxpubbylabel=watcher\naction_getactivewatchesbyxpub=watcher\naction_getactivewatchesbylabel=watcher\naction_getactivexpubwatches=watcher\naction_watchtxid=watcher\naction_getactivewatches=watcher\naction_getbestblockhash=watcher\naction_getbestblockinfo=watcher\naction_getblockinfo=watcher\naction_gettransaction=watcher\naction_ln_getinfo=watcher\naction_ln_create_invoice=watcher\naction_ln_getconnectionstring=watcher\naction_ln_decodebolt11=watcher\n\n# Spender can do what the watcher can do, plus:\naction_getbalance=spender\naction_getbalancebyxpub=spender\naction_getbalancebyxpublabel=spender\naction_getnewaddress=spender\naction_spend=spender\naction_addtobatch=spender\naction_batchspend=spender\naction_deriveindex=spender\naction_derivepubpath=spender\naction_ln_pay=spender\naction_ln_newaddr=spender\naction_ots_stamp=spender\naction_ots_getfile=spender\naction_ln_getinvoice=spender\naction_ln_decodebolt11=spender\naction_ln_connectfund=spender\n\n# Admin can do what the spender can do, plus:\n\n\n# Should be called from inside the Docker network only:\naction_conf=internal\naction_newblock=internal\naction_executecallbacks=internal\naction_ots_backoffice=internal"
|
||||
]
|
||||
},
|
||||
"gatekeeper_keys": {
|
||||
"$id": "#/properties/gatekeeper_keys",
|
||||
"type": "object",
|
||||
"title": "Gatekeeper keys",
|
||||
"default": {
|
||||
"configEntries": [],
|
||||
"clientInformation": []
|
||||
},
|
||||
"required": [
|
||||
"configEntries",
|
||||
"clientInformation"
|
||||
],
|
||||
"properties": {
|
||||
"configEntries": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries/entry",
|
||||
"type": "string",
|
||||
"pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
]
|
||||
]
|
||||
},
|
||||
"clientInformation": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation/entry",
|
||||
"type": "string",
|
||||
"pattern": "^.+=.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gatekeeper_clientkeyspassword": {
|
||||
"$id": "#/properties/gatekeeper_clientkeyspassword",
|
||||
"type": "string",
|
||||
"title": "Password for the encrypted client keys archive"
|
||||
},
|
||||
"gatekeeper_clientkeyspassword_c": {
|
||||
"$id": "#/properties/gatekeeper_clientkeyspassword",
|
||||
"type": "string",
|
||||
"title": "Password for the encrypted client keys archive, verified"
|
||||
},
|
||||
"gatekeeper_statuspw": {
|
||||
"$id": "#/properties/adminhash",
|
||||
"type": "string",
|
||||
"title": "MD5 hash of admin password"
|
||||
},
|
||||
"gatekeeper_sslcert": {
|
||||
"$id": "#/properties/gatekeeper_sslcert",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Cert"
|
||||
},
|
||||
"gatekeeper_sslkey": {
|
||||
"$id": "#/properties/gatekeeper_sslkey",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Key"
|
||||
},
|
||||
"gatekeeper_cns": {
|
||||
"$id": "#/properties/gatekeeper_cns",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper cns",
|
||||
"examples": [
|
||||
"myhost.mydomain.com,*.myotherdomain.com,123.123.123.123"
|
||||
]
|
||||
},
|
||||
"lightning_implementation": {
|
||||
"$id": "#/properties/lightning_implementation",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"c-lightning"
|
||||
],
|
||||
"title": "The lightning implementation",
|
||||
"default": "c-lightning",
|
||||
"examples": [
|
||||
"c-lightning"
|
||||
]
|
||||
},
|
||||
"lightning_nodename": {
|
||||
"$id": "#/properties/lightning_nodename",
|
||||
"type": "string",
|
||||
"title": "The lightning node name",
|
||||
"examples": [
|
||||
"🚀 Mighty Moose 🚀"
|
||||
]
|
||||
},
|
||||
"lightning_nodecolor": {
|
||||
"$id": "#/properties/lightning_nodecolor",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9A-Fa-f]{6}$",
|
||||
"title": "The lightning node color",
|
||||
"default": "ff0000",
|
||||
"examples": [
|
||||
"ff0000",
|
||||
"00ff00",
|
||||
"00ffff"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
515
cyphernodeconf_docker/schema/config-v0.2.0.json
Normal file
515
cyphernodeconf_docker/schema/config-v0.2.0.json
Normal file
@@ -0,0 +1,515 @@
|
||||
{
|
||||
"definitions": {},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://cyphernode.io/config-v0.2.0.json",
|
||||
"type": "object",
|
||||
"title": "Cyphernode config file structure v0.2.0",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"setup_version",
|
||||
"features",
|
||||
"net",
|
||||
"use_xpub",
|
||||
"installer_mode",
|
||||
"run_as_different_user",
|
||||
"docker_mode",
|
||||
"docker_versions",
|
||||
"adminhash",
|
||||
"bitcoin_rpcuser",
|
||||
"bitcoin_rpcpassword",
|
||||
"bitcoin_prune",
|
||||
"bitcoin_datapath",
|
||||
"bitcoin_mode",
|
||||
"bitcoin_expose",
|
||||
"gatekeeper_keys",
|
||||
"gatekeeper_sslcert",
|
||||
"gatekeeper_sslkey",
|
||||
"gatekeeper_cns",
|
||||
"gatekeeper_clientkeyspassword",
|
||||
"gatekeeper_datapath",
|
||||
"gatekeeper_port",
|
||||
"lightning_expose",
|
||||
"lightning_implementation",
|
||||
"lightning_datapath",
|
||||
"lightning_nodename",
|
||||
"lightning_nodecolor",
|
||||
"proxy_datapath",
|
||||
"otsclient_datapath",
|
||||
"traefik_datapath"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"run_as_different_user": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"username"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"use_xpub": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"xpub",
|
||||
"derivation_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"bitcoin_prune": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"bitcoin_prune_size"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"0.2.0"
|
||||
],
|
||||
"default": "0.2.0",
|
||||
"examples": [
|
||||
"0.2.0"
|
||||
]
|
||||
},
|
||||
"setup_version": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"v0.2.0"
|
||||
]
|
||||
},
|
||||
"docker_versions": {
|
||||
"$id": "#/properties/dockerVersions",
|
||||
"type": "object",
|
||||
"title": "All versions of the docker containers",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"$id": "#/properties/features",
|
||||
"type": "array",
|
||||
"title": "The optional features of this cyphernode",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$id": "#/properties/features/items",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"title": "The feature",
|
||||
"default": "",
|
||||
"examples": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
]
|
||||
}
|
||||
},
|
||||
"net": {
|
||||
"$id": "#/properties/net",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"testnet",
|
||||
"mainnet"
|
||||
],
|
||||
"title": "The net cyphernode is running on",
|
||||
"default": "testnet",
|
||||
"examples": [
|
||||
"testnet"
|
||||
]
|
||||
},
|
||||
"use_xpub": {
|
||||
"$id": "#/properties/use_xpub",
|
||||
"type": "boolean",
|
||||
"title": "Use xpub key?",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"xpub": {
|
||||
"$id": "#/properties/xpub",
|
||||
"type": "string",
|
||||
"title": "Default xpub to derive addresses from",
|
||||
"pattern": "^(\\w+)$"
|
||||
},
|
||||
"derivation_path": {
|
||||
"$id": "#/properties/derivation_path",
|
||||
"type": "string",
|
||||
"title": "Default derivation path",
|
||||
"default": "0/n",
|
||||
"examples": [
|
||||
"0/n"
|
||||
]
|
||||
},
|
||||
"installer_mode": {
|
||||
"$id": "#/properties/installer_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"docker"
|
||||
],
|
||||
"title": "Install mode",
|
||||
"default": "docker",
|
||||
"examples": [
|
||||
"docker"
|
||||
]
|
||||
},
|
||||
"run_as_different_user": {
|
||||
"$id": "#/properties/run_as_different_user",
|
||||
"type": "boolean",
|
||||
"title": "Run as different user",
|
||||
"default": true,
|
||||
"examples": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"username": {
|
||||
"$id": "#/properties/username",
|
||||
"type": "string",
|
||||
"title": "Username to run under",
|
||||
"default": "cyphernode",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"docker_mode": {
|
||||
"$id": "#/properties/docker_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"swarm",
|
||||
"compose"
|
||||
],
|
||||
"title": "How to run the containers",
|
||||
"default": "compose",
|
||||
"examples": [
|
||||
"compose"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcuser": {
|
||||
"$id": "#/properties/bitcoin_rpcuser",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc user",
|
||||
"default": "bitcoin",
|
||||
"examples": [
|
||||
"bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcpassword": {
|
||||
"$id": "#/properties/bitcoin_rpcpassword",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc password",
|
||||
"default": "CHANGEME",
|
||||
"examples": [
|
||||
"CHANGEME"
|
||||
]
|
||||
},
|
||||
"bitcoin_uacomment": {
|
||||
"$id": "#/properties/bitcoin_uacomment",
|
||||
"type": "string",
|
||||
"title": "Bitcoin user agent comment",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune": {
|
||||
"$id": "#/properties/bitcoin_prune",
|
||||
"type": "boolean",
|
||||
"title": "Bitcoin prune",
|
||||
"default": false,
|
||||
"examples": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune_size": {
|
||||
"$id": "#/properties/bitcoin_prune_size",
|
||||
"type": "integer",
|
||||
"title": "Bitcoin prune size",
|
||||
"default": 550,
|
||||
"examples": [
|
||||
550
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath": {
|
||||
"$id": "#/properties/bitcoin_datapath",
|
||||
"type": "string",
|
||||
"title": "Bitcoin datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath_custom": {
|
||||
"$id": "#/properties/bitcoin_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Bitcoin custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"lightning_datapath": {
|
||||
"$id": "#/properties/lightning_datapath",
|
||||
"type": "string",
|
||||
"title": "Lightning datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"lightning_datapath_custom": {
|
||||
"$id": "#/properties/lightning_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Lightning custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"proxy_datapath": {
|
||||
"$id": "#/properties/proxy_datapath",
|
||||
"type": "string",
|
||||
"title": "Proxy datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"proxy_datapath_custom": {
|
||||
"$id": "#/properties/proxy_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Proxy custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath": {
|
||||
"$id": "#/properties/otsclient_datapath",
|
||||
"type": "string",
|
||||
"title": "OTS Client datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath_custom": {
|
||||
"$id": "#/properties/otsclient_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "OTS Client custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"traefik_datapath": {
|
||||
"$id": "#/properties/traefik_datapath",
|
||||
"type": "string",
|
||||
"title": "Traefik datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/traefik"
|
||||
]
|
||||
},
|
||||
"traefik_datapath_custom": {
|
||||
"$id": "#/properties/traefik_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Traefik custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/traefik"
|
||||
]
|
||||
},
|
||||
"lightning_external_ip": {
|
||||
"$id": "#/properties/lightning_external_ip",
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"title": "External lightning node ip",
|
||||
"examples": [
|
||||
"123.123.123.123"
|
||||
]
|
||||
},
|
||||
"bitcoin_mode": {
|
||||
"$id": "#/properties/bitcoin_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal"
|
||||
],
|
||||
"title": "Bitcoin mode",
|
||||
"default": "internal",
|
||||
"examples": [
|
||||
"internal"
|
||||
]
|
||||
},
|
||||
"bitcoin_expose": {
|
||||
"$id": "#/properties/bitcoin_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose bitcoin node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"lightning_expose": {
|
||||
"$id": "#/properties/lightning_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose lightning node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath": {
|
||||
"$id": "#/properties/gatekeeper_datapath",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath_custom": {
|
||||
"$id": "#/properties/gatekeeper_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_port": {
|
||||
"$id": "#/properties/gatekeeper_port",
|
||||
"type": "integer",
|
||||
"title": "Gatekeeper port",
|
||||
"default": 2009,
|
||||
"examples": [
|
||||
2009
|
||||
]
|
||||
},
|
||||
"gatekeeper_keys": {
|
||||
"$id": "#/properties/gatekeeper_keys",
|
||||
"type": "object",
|
||||
"title": "Gatekeeper keys",
|
||||
"default": {
|
||||
"configEntries": [],
|
||||
"clientInformation": []
|
||||
},
|
||||
"required": [
|
||||
"configEntries",
|
||||
"clientInformation"
|
||||
],
|
||||
"properties": {
|
||||
"configEntries": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries/entry",
|
||||
"type": "string",
|
||||
"pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats,watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"stats,watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"stats,watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
]
|
||||
]
|
||||
},
|
||||
"clientInformation": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation/entry",
|
||||
"type": "string",
|
||||
"pattern": "^.+=.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"000=a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gatekeeper_sslcert": {
|
||||
"$id": "#/properties/gatekeeper_sslcert",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Cert"
|
||||
},
|
||||
"gatekeeper_sslkey": {
|
||||
"$id": "#/properties/gatekeeper_sslkey",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Key"
|
||||
},
|
||||
"gatekeeper_cns": {
|
||||
"$id": "#/properties/gatekeeper_cns",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper cns",
|
||||
"examples": [
|
||||
"myhost.mydomain.com,*.myotherdomain.com,123.123.123.123"
|
||||
]
|
||||
},
|
||||
"gatekeeper_clientkeyspassword": {
|
||||
"$id": "#/properties/gatekeeper_clientkeyspassword",
|
||||
"type": "string",
|
||||
"title": "Password for the encrypted client keys archive"
|
||||
},
|
||||
"adminhash": {
|
||||
"$id": "#/properties/adminhash",
|
||||
"type": "string",
|
||||
"title": "Bcrypted hash of admin password"
|
||||
},
|
||||
"lightning_implementation": {
|
||||
"$id": "#/properties/lightning_implementation",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"c-lightning"
|
||||
],
|
||||
"title": "The lightning implementation",
|
||||
"default": "c-lightning",
|
||||
"examples": [
|
||||
"c-lightning"
|
||||
]
|
||||
},
|
||||
"lightning_nodename": {
|
||||
"$id": "#/properties/lightning_nodename",
|
||||
"type": "string",
|
||||
"title": "The lightning node name",
|
||||
"examples": [
|
||||
"🚀 Mighty Moose 🚀"
|
||||
]
|
||||
},
|
||||
"lightning_nodecolor": {
|
||||
"$id": "#/properties/lightning_nodecolor",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9A-Fa-f]{6}$",
|
||||
"title": "The lightning node color",
|
||||
"examples": [
|
||||
"ff0000",
|
||||
"00ff00",
|
||||
"00ffff"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
590
cyphernodeconf_docker/schema/config-v0.2.2.json
Normal file
590
cyphernodeconf_docker/schema/config-v0.2.2.json
Normal file
@@ -0,0 +1,590 @@
|
||||
{
|
||||
"definitions": {},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://cyphernode.io/config-v0.2.2.json",
|
||||
"type": "object",
|
||||
"title": "Cyphernode config file structure v0.2.2",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"setup_version",
|
||||
"features",
|
||||
"net",
|
||||
"use_xpub",
|
||||
"installer_mode",
|
||||
"run_as_different_user",
|
||||
"docker_mode",
|
||||
"docker_versions",
|
||||
"adminhash",
|
||||
"bitcoin_rpcuser",
|
||||
"bitcoin_rpcpassword",
|
||||
"bitcoin_prune",
|
||||
"bitcoin_datapath",
|
||||
"bitcoin_mode",
|
||||
"bitcoin_expose",
|
||||
"gatekeeper_expose",
|
||||
"gatekeeper_keys",
|
||||
"gatekeeper_sslcert",
|
||||
"gatekeeper_sslkey",
|
||||
"gatekeeper_cns",
|
||||
"gatekeeper_clientkeyspassword",
|
||||
"gatekeeper_datapath",
|
||||
"gatekeeper_port",
|
||||
"proxy_datapath",
|
||||
"traefik_datapath",
|
||||
"traefik_http_port",
|
||||
"traefik_https_port"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"run_as_different_user": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"username"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"use_xpub": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"xpub",
|
||||
"derivation_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"bitcoin_prune": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"bitcoin_prune_size"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"features": {
|
||||
"contains": {
|
||||
"enum": [
|
||||
"lightning"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"lightning_announce",
|
||||
"lightning_expose",
|
||||
"lightning_implementation",
|
||||
"lightning_datapath",
|
||||
"lightning_nodename",
|
||||
"lightning_nodecolor"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"features": {
|
||||
"contains": {
|
||||
"enum": [
|
||||
"otsclient"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"otsclient_datapath"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"0.2.2"
|
||||
],
|
||||
"default": "0.3.0",
|
||||
"examples": [
|
||||
"0.2.2"
|
||||
]
|
||||
},
|
||||
"setup_version": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"v0.2.0"
|
||||
]
|
||||
},
|
||||
"docker_versions": {
|
||||
"$id": "#/properties/dockerVersions",
|
||||
"type": "object",
|
||||
"title": "All versions of the docker containers",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"$id": "#/properties/features",
|
||||
"type": "array",
|
||||
"title": "The optional features of this cyphernode",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$id": "#/properties/features/items",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"title": "The feature",
|
||||
"default": "",
|
||||
"examples": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
]
|
||||
}
|
||||
},
|
||||
"net": {
|
||||
"$id": "#/properties/net",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"testnet",
|
||||
"mainnet",
|
||||
"regtest"
|
||||
],
|
||||
"title": "The net cyphernode is running on",
|
||||
"default": "testnet",
|
||||
"examples": [
|
||||
"testnet"
|
||||
]
|
||||
},
|
||||
"use_xpub": {
|
||||
"$id": "#/properties/use_xpub",
|
||||
"type": "boolean",
|
||||
"title": "Use xpub key?",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"xpub": {
|
||||
"$id": "#/properties/xpub",
|
||||
"type": "string",
|
||||
"title": "Default xpub to derive addresses from",
|
||||
"pattern": "^(\\w+)$"
|
||||
},
|
||||
"derivation_path": {
|
||||
"$id": "#/properties/derivation_path",
|
||||
"type": "string",
|
||||
"title": "Default derivation path",
|
||||
"default": "0/n",
|
||||
"examples": [
|
||||
"0/n"
|
||||
]
|
||||
},
|
||||
"installer_mode": {
|
||||
"$id": "#/properties/installer_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"docker"
|
||||
],
|
||||
"title": "Install mode",
|
||||
"default": "docker",
|
||||
"examples": [
|
||||
"docker"
|
||||
]
|
||||
},
|
||||
"run_as_different_user": {
|
||||
"$id": "#/properties/run_as_different_user",
|
||||
"type": "boolean",
|
||||
"title": "Run as different user",
|
||||
"default": true,
|
||||
"examples": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"username": {
|
||||
"$id": "#/properties/username",
|
||||
"type": "string",
|
||||
"title": "Username to run under",
|
||||
"default": "cyphernode",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"docker_mode": {
|
||||
"$id": "#/properties/docker_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"swarm",
|
||||
"compose"
|
||||
],
|
||||
"title": "How to run the containers",
|
||||
"default": "compose",
|
||||
"examples": [
|
||||
"compose"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcuser": {
|
||||
"$id": "#/properties/bitcoin_rpcuser",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc user",
|
||||
"default": "bitcoin",
|
||||
"examples": [
|
||||
"bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_rpcpassword": {
|
||||
"$id": "#/properties/bitcoin_rpcpassword",
|
||||
"type": "string",
|
||||
"title": "Bitcoin rpc password",
|
||||
"default": "CHANGEME",
|
||||
"examples": [
|
||||
"CHANGEME"
|
||||
]
|
||||
},
|
||||
"bitcoin_uacomment": {
|
||||
"$id": "#/properties/bitcoin_uacomment",
|
||||
"type": "string",
|
||||
"title": "Bitcoin user agent comment",
|
||||
"examples": [
|
||||
"cyphernode"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune": {
|
||||
"$id": "#/properties/bitcoin_prune",
|
||||
"type": "boolean",
|
||||
"title": "Bitcoin prune",
|
||||
"default": false,
|
||||
"examples": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
"bitcoin_prune_size": {
|
||||
"$id": "#/properties/bitcoin_prune_size",
|
||||
"type": "integer",
|
||||
"title": "Bitcoin prune size",
|
||||
"default": 550,
|
||||
"examples": [
|
||||
550
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath": {
|
||||
"$id": "#/properties/bitcoin_datapath",
|
||||
"type": "string",
|
||||
"title": "Bitcoin datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"bitcoin_datapath_custom": {
|
||||
"$id": "#/properties/bitcoin_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Bitcoin custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/bitcoin"
|
||||
]
|
||||
},
|
||||
"lightning_datapath": {
|
||||
"$id": "#/properties/lightning_datapath",
|
||||
"type": "string",
|
||||
"title": "Lightning datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"lightning_datapath_custom": {
|
||||
"$id": "#/properties/lightning_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Lightning custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/lightning"
|
||||
]
|
||||
},
|
||||
"proxy_datapath": {
|
||||
"$id": "#/properties/proxy_datapath",
|
||||
"type": "string",
|
||||
"title": "Proxy datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"proxy_datapath_custom": {
|
||||
"$id": "#/properties/proxy_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Proxy custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/proxy"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath": {
|
||||
"$id": "#/properties/otsclient_datapath",
|
||||
"type": "string",
|
||||
"title": "OTS Client datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"otsclient_datapath_custom": {
|
||||
"$id": "#/properties/otsclient_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "OTS Client custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/otsclient"
|
||||
]
|
||||
},
|
||||
"traefik_http_port": {
|
||||
"$id": "#/properties/traefik_port",
|
||||
"type": "integer",
|
||||
"title": "Traefik HTTP port",
|
||||
"default": 80,
|
||||
"examples": [
|
||||
80
|
||||
]
|
||||
},
|
||||
"traefik_https_port": {
|
||||
"$id": "#/properties/traefik_https_port",
|
||||
"type": "integer",
|
||||
"title": "Traefik HTTPS port",
|
||||
"default": 443,
|
||||
"examples": [
|
||||
443
|
||||
]
|
||||
},
|
||||
"traefik_datapath": {
|
||||
"$id": "#/properties/traefik_datapath",
|
||||
"type": "string",
|
||||
"title": "Traefik datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/traefik"
|
||||
]
|
||||
},
|
||||
"traefik_datapath_custom": {
|
||||
"$id": "#/properties/traefik_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Traefik custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/traefik"
|
||||
]
|
||||
},
|
||||
"lightning_announce": {
|
||||
"$id": "#/properties/lightning_announce",
|
||||
"type": "boolean",
|
||||
"title": "Announce lightning ip",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"lightning_external_ip": {
|
||||
"$id": "#/properties/lightning_external_ip",
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"title": "External lightning node ip",
|
||||
"examples": [
|
||||
"123.123.123.123"
|
||||
]
|
||||
},
|
||||
"bitcoin_mode": {
|
||||
"$id": "#/properties/bitcoin_mode",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal"
|
||||
],
|
||||
"title": "Bitcoin mode",
|
||||
"default": "internal",
|
||||
"examples": [
|
||||
"internal"
|
||||
]
|
||||
},
|
||||
"bitcoin_expose": {
|
||||
"$id": "#/properties/bitcoin_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose bitcoin node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"lightning_expose": {
|
||||
"$id": "#/properties/lightning_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose lightning node",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"gatekeeper_expose": {
|
||||
"$id": "#/properties/gatekeeper_expose",
|
||||
"type": "boolean",
|
||||
"title": "Expose gatekeeper port",
|
||||
"default": false,
|
||||
"examples": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath": {
|
||||
"$id": "#/properties/gatekeeper_datapath",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_datapath_custom": {
|
||||
"$id": "#/properties/gatekeeper_datapath_custom",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper custom datapath",
|
||||
"examples": [
|
||||
"/tmp/cyphernode/gatekeeper"
|
||||
]
|
||||
},
|
||||
"gatekeeper_port": {
|
||||
"$id": "#/properties/gatekeeper_port",
|
||||
"type": "integer",
|
||||
"title": "Gatekeeper port",
|
||||
"default": 2009,
|
||||
"examples": [
|
||||
2009
|
||||
]
|
||||
},
|
||||
"gatekeeper_keys": {
|
||||
"$id": "#/properties/gatekeeper_keys",
|
||||
"type": "object",
|
||||
"title": "Gatekeeper keys",
|
||||
"default": {
|
||||
"configEntries": [],
|
||||
"clientInformation": []
|
||||
},
|
||||
"required": [
|
||||
"configEntries",
|
||||
"clientInformation"
|
||||
],
|
||||
"properties": {
|
||||
"configEntries": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/configEntries/entry",
|
||||
"type": "string",
|
||||
"pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats,watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"stats,watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"stats,watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
]
|
||||
]
|
||||
},
|
||||
"clientInformation": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$id": "#/properties/gatekeeper_keys/clientInformation/entry",
|
||||
"type": "string",
|
||||
"pattern": "^.+=.+$"
|
||||
},
|
||||
"examples": [
|
||||
[
|
||||
"000=a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gatekeeper_sslcert": {
|
||||
"$id": "#/properties/gatekeeper_sslcert",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Cert"
|
||||
},
|
||||
"gatekeeper_sslkey": {
|
||||
"$id": "#/properties/gatekeeper_sslkey",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper SSL Key"
|
||||
},
|
||||
"gatekeeper_cns": {
|
||||
"$id": "#/properties/gatekeeper_cns",
|
||||
"type": "string",
|
||||
"title": "Gatekeeper cns",
|
||||
"examples": [
|
||||
"myhost.mydomain.com,*.myotherdomain.com,123.123.123.123"
|
||||
]
|
||||
},
|
||||
"gatekeeper_clientkeyspassword": {
|
||||
"$id": "#/properties/gatekeeper_clientkeyspassword",
|
||||
"type": "string",
|
||||
"title": "Password for the encrypted client keys archive"
|
||||
},
|
||||
"adminhash": {
|
||||
"$id": "#/properties/adminhash",
|
||||
"type": "string",
|
||||
"title": "Bcrypted hash of admin password"
|
||||
},
|
||||
"lightning_implementation": {
|
||||
"$id": "#/properties/lightning_implementation",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"c-lightning"
|
||||
],
|
||||
"title": "The lightning implementation",
|
||||
"default": "c-lightning",
|
||||
"examples": [
|
||||
"c-lightning"
|
||||
]
|
||||
},
|
||||
"lightning_nodename": {
|
||||
"$id": "#/properties/lightning_nodename",
|
||||
"type": "string",
|
||||
"title": "The lightning node name",
|
||||
"examples": [
|
||||
"🚀 Mighty Moose 🚀"
|
||||
]
|
||||
},
|
||||
"lightning_nodecolor": {
|
||||
"$id": "#/properties/lightning_nodecolor",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9A-Fa-f]{6}$",
|
||||
"title": "The lightning node color",
|
||||
"examples": [
|
||||
"ff0000",
|
||||
"00ff00",
|
||||
"00ffff"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
<% if (net === 'testnet') { %>
|
||||
# testnet
|
||||
testnet=1
|
||||
<% } else if( net === 'regtest' ) { %>
|
||||
# regtest
|
||||
regtest=1
|
||||
<% } %>
|
||||
|
||||
rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>
|
||||
@@ -1,9 +1,12 @@
|
||||
<% if (net === 'testnet') { %>
|
||||
# testnet
|
||||
testnet=1
|
||||
<% } else if( net === 'regtest' ) { %>
|
||||
# regtest
|
||||
regtest=1
|
||||
<% } %>
|
||||
|
||||
<% if (bitcoin_prune) { %>
|
||||
<% if (bitcoin_prune && bitcoin_mode === 'internal') { %>
|
||||
prune=<%= bitcoin_prune_size || 550 %>
|
||||
<% } else { %>
|
||||
txindex=1
|
||||
@@ -28,16 +31,31 @@ server=1
|
||||
|
||||
<% if (net === 'testnet') { %>
|
||||
test.wallet=watching01.dat
|
||||
test.wallet=xpubwatching01.dat
|
||||
test.wallet=spending01.dat
|
||||
test.wallet=ln01.dat
|
||||
test.rpcbind=0.0.0.0
|
||||
<% } else if ( net === 'regtest' ) { %>
|
||||
regtest.wallet=watching01.dat
|
||||
regtest.wallet=xpubwatching01.dat
|
||||
regtest.wallet=spending01.dat
|
||||
regtest.wallet=ln01.dat
|
||||
regtest.rpcbind=0.0.0.0
|
||||
regtest.rpcport=18443
|
||||
<% } else { %>
|
||||
main.wallet=watching01.dat
|
||||
main.wallet=xpubwatching01.dat
|
||||
main.wallet=spending01.dat
|
||||
main.wallet=ln01.dat
|
||||
main.rpcbind=0.0.0.0
|
||||
<% } %>
|
||||
|
||||
walletnotify=curl proxy:8888/conf/%s
|
||||
walletnotify=/usr/bin/curl proxy:8888/conf/%s
|
||||
blocknotify=/usr/bin/curl proxy:8888/newblock/%s
|
||||
|
||||
<% if ( bitcoin_uacomment != null && bitcoin_uacomment != '' ) { %>
|
||||
uacomment=<%= bitcoin_uacomment %>
|
||||
<% } %>
|
||||
|
||||
addresstype=bech32
|
||||
walletrbf=1
|
||||
1
cyphernodeconf_docker/templates/cyphernode/info.json
Normal file
1
cyphernodeconf_docker/templates/cyphernode/info.json
Normal file
@@ -0,0 +1 @@
|
||||
<%- JSON.stringify( installationInfo, null, 2 ) %>
|
||||
60
cyphernodeconf_docker/templates/gatekeeper/api.properties
Normal file
60
cyphernodeconf_docker/templates/gatekeeper/api.properties
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
# Watcher can do stuff
|
||||
# Spender can do what the watcher can do plus more stuff
|
||||
# Admin can do what the spender can do plus even more stuff
|
||||
|
||||
# Stats can:
|
||||
action_helloworld=stats
|
||||
action_getblockchaininfo=stats
|
||||
action_installation_info=stats
|
||||
action_getmempoolinfo=stats
|
||||
|
||||
# Watcher can:
|
||||
action_watch=watcher
|
||||
action_unwatch=watcher
|
||||
action_watchxpub=watcher
|
||||
action_unwatchxpubbyxpub=watcher
|
||||
action_unwatchxpubbylabel=watcher
|
||||
action_getactivewatchesbyxpub=watcher
|
||||
action_getactivewatchesbylabel=watcher
|
||||
action_getactivexpubwatches=watcher
|
||||
action_watchtxid=watcher
|
||||
action_getactivewatches=watcher
|
||||
action_getbestblockhash=watcher
|
||||
action_getbestblockinfo=watcher
|
||||
action_getblockinfo=watcher
|
||||
action_gettransaction=watcher
|
||||
action_ots_verify=watcher
|
||||
action_ots_info=watcher
|
||||
action_ln_getinfo=watcher
|
||||
action_ln_create_invoice=watcher
|
||||
action_ln_getconnectionstring=watcher
|
||||
action_ln_decodebolt11=watcher
|
||||
|
||||
# Spender can do what the watcher can do, plus:
|
||||
action_getbalance=spender
|
||||
action_getbalancebyxpub=spender
|
||||
action_getbalancebyxpublabel=spender
|
||||
action_getnewaddress=spender
|
||||
action_spend=spender
|
||||
action_bumpfee=spender
|
||||
action_addtobatch=spender
|
||||
action_batchspend=spender
|
||||
action_deriveindex=spender
|
||||
action_derivepubpath=spender
|
||||
action_ln_pay=spender
|
||||
action_ln_newaddr=spender
|
||||
action_ots_stamp=spender
|
||||
action_ots_getfile=spender
|
||||
action_ln_getinvoice=spender
|
||||
action_ln_decodebolt11=spender
|
||||
action_ln_connectfund=spender
|
||||
|
||||
# Admin can do what the spender can do, plus:
|
||||
|
||||
|
||||
# Should be called from inside the Docker network only:
|
||||
action_conf=internal
|
||||
action_newblock=internal
|
||||
action_executecallbacks=internal
|
||||
action_ots_backoffice=internal
|
||||
@@ -1,22 +1,25 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen <%= gatekeeper_port %> ssl;
|
||||
server_name localhost;
|
||||
|
||||
#include /etc/nginx/conf.d/ip-whitelist.conf;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/cert.pem;
|
||||
ssl_certificate_key /etc/ssl/private/key.pem;
|
||||
|
||||
location /status {
|
||||
auth_basic "status";
|
||||
auth_basic_user_file conf.d/status/htpasswd;
|
||||
location /s/ {
|
||||
auth_request /auth;
|
||||
root /etc/nginx/conf.d;
|
||||
index statuspage.html;
|
||||
}
|
||||
|
||||
location /v0/ {
|
||||
auth_request /auth;
|
||||
proxy_pass http://proxy:8888/;
|
||||
|
||||
# Up default 60 second timeout for 3 minutes (OTS stamping can take time)
|
||||
proxy_connect_timeout 180;
|
||||
proxy_send_timeout 180;
|
||||
proxy_read_timeout 180;
|
||||
send_timeout 180;
|
||||
|
||||
}
|
||||
|
||||
location /auth {
|
||||
@@ -5,9 +5,12 @@ FEATURE_OTSCLIENT=<%= (features.indexOf('otsclient') != -1)?'true':'false' %>
|
||||
LIGHTNING_IMPLEMENTATION=<%= lightning_implementation %>
|
||||
PROXY_DATAPATH=<%= proxy_datapath %>
|
||||
GATEKEEPER_DATAPATH=<%= gatekeeper_datapath %>
|
||||
GATEKEEPER_PORT=<%= gatekeeper_port %>
|
||||
TRAEFIK_DATAPATH=<%= traefik_datapath %>
|
||||
DOCKER_MODE=<%= docker_mode %>
|
||||
RUN_AS_USER=<%= run_as_different_user?username:'' %>
|
||||
CLEANUP=<%= installer_cleanup?'true':'false' %>
|
||||
SHARED_HTPASSWD_PATH=<%= traefik_datapath %>/htpasswd
|
||||
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
|
||||
LIGHTNING_DATAPATH=<%= lightning_datapath %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,268 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
|
||||
<% if( bitcoin_mode === 'internal' ) { %>
|
||||
##########################
|
||||
# BITCOIN #
|
||||
##########################
|
||||
|
||||
bitcoin:
|
||||
image: cyphernode/bitcoin:<%= bitcoin_version %>
|
||||
command: $USER bitcoind
|
||||
<% if( bitcoin_expose ) { %>
|
||||
ports:
|
||||
- "<%= (net === 'regtest') ? '18443:18443' : ((net === 'testnet') ? '18332:18332' : '8332:8332') %>"
|
||||
- "<%= (net === 'regtest') ? '18444:18444' : ((net === 'testnet') ? '18333:18333' : '8333:8333') %>"
|
||||
<% } %>
|
||||
volumes:
|
||||
- "<%= bitcoin_datapath %>:/.bitcoin"
|
||||
- bitcoin_monitor:/bitcoin_monitor
|
||||
healthcheck:
|
||||
test: bitcoin-cli echo && touch /bitcoin_monitor/up || rm -f /bitcoin_monitor/up
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
<% } %>
|
||||
|
||||
##########################
|
||||
# PROXY #
|
||||
##########################
|
||||
|
||||
proxy:
|
||||
image: cyphernode/proxy:<%= proxy_version %>
|
||||
command: $USER ./startproxy.sh
|
||||
environment:
|
||||
- "TRACING=1"
|
||||
- "WATCHER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal') ? 'bitcoin' : bitcoin_node_ip %>:<%= (net === 'regtest') ? '18443' : ((net === 'testnet') ? '18332' : '8332') %>/wallet"
|
||||
- "WATCHER_BTC_NODE_DEFAULT_WALLET=watching01.dat"
|
||||
- "WATCHER_BTC_NODE_XPUB_WALLET=xpubwatching01.dat"
|
||||
- "WATCHER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>"
|
||||
- "WATCHER_BTC_NODE_RPC_CFG=/tmp/watcher_btcnode_curlcfg.properties"
|
||||
- "SPENDER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal') ? 'bitcoin' : bitcoin_node_ip %>:<%= (net === 'regtest') ? '18443' : ((net === 'testnet') ? '18332' : '8332') %>/wallet"
|
||||
- "SPENDER_BTC_NODE_DEFAULT_WALLET=spending01.dat"
|
||||
- "SPENDER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>"
|
||||
- "SPENDER_BTC_NODE_RPC_CFG=/tmp/spender_btcnode_curlcfg.properties"
|
||||
- "PROXY_LISTENING_PORT=8888"
|
||||
- "DB_PATH=/proxy/db"
|
||||
- "DB_FILE=/proxy/db/proxydb"
|
||||
- "PYCOIN_CONTAINER=pycoin:7777"
|
||||
<% if ( use_xpub && xpub ) { %>
|
||||
- "DERIVATION_PUB32=<%= xpub %>"
|
||||
- "DERIVATION_PATH=<%= derivation_path %>"
|
||||
<% } %>
|
||||
- "WATCHER_BTC_NODE_PRUNED=<%= bitcoin_prune ? 'true' : 'false' %>"
|
||||
- "OTSCLIENT_CONTAINER=otsclient:6666"
|
||||
- "OTS_FILES=/proxy/otsfiles"
|
||||
- "XPUB_DERIVATION_GAP=100"
|
||||
<% if ( devmode ) { %>
|
||||
ports:
|
||||
- "8888:8888"
|
||||
<% } %>
|
||||
volumes:
|
||||
- "<%= proxy_datapath %>:/proxy/db"
|
||||
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
|
||||
- "<%= lightning_datapath %>:/.lightning"
|
||||
<% } %>
|
||||
<% if ( features.indexOf('otsclient') !== -1 ) { %>
|
||||
- "<%= otsclient_datapath %>:/proxy/otsfiles"
|
||||
<% } %>
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
##########################
|
||||
# PROXYCRON #
|
||||
##########################
|
||||
|
||||
proxycron:
|
||||
image: cyphernode/proxycron:<%= proxycron_version %>
|
||||
environment:
|
||||
- "TX_CONF_URL=proxy:8888/executecallbacks"
|
||||
- "OTS_URL=proxy:8888/ots_backoffice"
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
depends_on:
|
||||
- proxy
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
##########################
|
||||
# BROKER #
|
||||
##########################
|
||||
|
||||
broker:
|
||||
image: eclipse-mosquitto:1.6
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
##########################
|
||||
# NOTIFIER #
|
||||
##########################
|
||||
|
||||
notifier:
|
||||
image: cyphernode/notifier:<%= notifier_version %>
|
||||
command: $USER ./startnotifier.sh
|
||||
networks:
|
||||
- cyphernodenet
|
||||
- cyphernodeappsnet
|
||||
restart: always
|
||||
depends_on:
|
||||
- broker
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
##########################
|
||||
# PYCOIN #
|
||||
##########################
|
||||
|
||||
pycoin:
|
||||
image: cyphernode/pycoin:<%= pycoin_version %>
|
||||
command: $USER ./startpycoin.sh
|
||||
environment:
|
||||
- "TRACING=1"
|
||||
- "PYCOIN_LISTENING_PORT=7777"
|
||||
<% if ( devmode ) { %>
|
||||
ports:
|
||||
- "7777:7777"
|
||||
<% } %>
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
<% if ( features.indexOf('otsclient') !== -1 ) { %>
|
||||
##########################
|
||||
# OTSCLIENT #
|
||||
##########################
|
||||
|
||||
otsclient:
|
||||
image: cyphernode/otsclient:<%= otsclient_version %>
|
||||
command: $USER /script/startotsclient.sh
|
||||
environment:
|
||||
- "TRACING=1"
|
||||
- "OTSCLIENT_LISTENING_PORT=6666"
|
||||
<% if (net === 'testnet') { %>
|
||||
- "TESTNET=1"
|
||||
<% } %>
|
||||
volumes:
|
||||
- "<%= otsclient_datapath %>:/otsfiles"
|
||||
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf"
|
||||
command: $USER /script/startotsclient.sh
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
<% } %>
|
||||
|
||||
##########################
|
||||
# GATEKEEPER #
|
||||
##########################
|
||||
|
||||
gatekeeper:
|
||||
# HTTP authentication API gate
|
||||
image: cyphernode/gatekeeper:<%= gatekeeper_version %>
|
||||
command: $USER
|
||||
environment:
|
||||
- "TRACING=1"
|
||||
<% if( gatekeeper_expose ) { %>
|
||||
ports:
|
||||
- "<%= gatekeeper_port %>:<%= gatekeeper_port %>"
|
||||
<% } %>
|
||||
volumes:
|
||||
- "<%= gatekeeper_datapath %>/certs:/etc/ssl/certs"
|
||||
- "<%= gatekeeper_datapath %>/private:/etc/ssl/private"
|
||||
- "<%= gatekeeper_datapath %>/keys.properties:/etc/nginx/conf.d/keys.properties"
|
||||
- "<%= gatekeeper_datapath %>/api.properties:/etc/nginx/conf.d/api.properties"
|
||||
- "<%= gatekeeper_datapath %>/default.conf:/etc/nginx/conf.d/default.conf"
|
||||
- "<%= gatekeeper_datapath %>/htpasswd:/etc/nginx/conf.d/status/htpasswd"
|
||||
- "<%= gatekeeper_datapath %>/installation.json:/etc/nginx/conf.d/s/stats/installation.json"
|
||||
- "<%= gatekeeper_datapath %>/client.7z:/etc/nginx/conf.d/s/stats/client.7z"
|
||||
- "<%= gatekeeper_datapath %>/config.7z:/etc/nginx/conf.d/s/stats/config.7z"
|
||||
networks:
|
||||
- cyphernodenet
|
||||
- cyphernodeappsnet
|
||||
restart: always
|
||||
depends_on:
|
||||
- proxy
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
##########################
|
||||
# TRAEFIK #
|
||||
##########################
|
||||
|
||||
traefik:
|
||||
image: traefik:v1.7.9-alpine
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "<%= traefik_datapath%>/traefik.toml:/traefik.toml"
|
||||
- "<%= traefik_datapath%>/acme.json:/acme.json"
|
||||
- "<%= traefik_datapath%>/htpasswd:/htpasswd/htpasswd"
|
||||
networks:
|
||||
- cyphernodeappsnet
|
||||
restart: always
|
||||
depends_on:
|
||||
- gatekeeper
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
|
||||
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
|
||||
##########################
|
||||
# LIGHTNING #
|
||||
##########################
|
||||
|
||||
lightning:
|
||||
image: cyphernode/clightning:<%= lightning_version %>
|
||||
command: $USER sh -c 'while [ ! -f "/bitcoin_monitor/up" ]; do echo "bitcoin not ready" ; sleep 10 ; done ; echo "bitcoin ready!" ; lightningd'
|
||||
<% if( lightning_expose ) { %>
|
||||
ports:
|
||||
- "9735:9735"
|
||||
<% } %>
|
||||
volumes:
|
||||
- "<%= lightning_datapath %>:/.lightning"
|
||||
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf"
|
||||
- bitcoin_monitor:/bitcoin_monitor:ro
|
||||
networks:
|
||||
- cyphernodenet
|
||||
restart: always
|
||||
depends_on:
|
||||
- bitcoin
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
<% } %>
|
||||
|
||||
volumes:
|
||||
bitcoin_monitor:
|
||||
|
||||
networks:
|
||||
cyphernodenet:
|
||||
external: true
|
||||
cyphernodeappsnet:
|
||||
external: true
|
||||
69
cyphernodeconf_docker/templates/installer/start.sh
Normal file
69
cyphernodeconf_docker/templates/installer/start.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./.cyphernodeconf/installer/config.sh
|
||||
|
||||
# be aware that randomly downloaded cyphernode apps will have access to
|
||||
# your configuration and filesystem.
|
||||
# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!!
|
||||
# TODO: Test if we can mitigate this security issue by
|
||||
# running app dockers inside a docker container
|
||||
|
||||
start_apps() {
|
||||
local SCRIPT_NAME="start.sh"
|
||||
local APP_SCRIPT_PATH
|
||||
local APP_START_SCRIPT_PATH
|
||||
local APP_ID
|
||||
|
||||
for i in $current_path/apps/*
|
||||
do
|
||||
APP_SCRIPT_PATH=$(echo $i)
|
||||
if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
|
||||
APP_START_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
|
||||
APP_ID=$(basename $APP_SCRIPT_PATH)
|
||||
|
||||
if [ -f "$APP_START_SCRIPT_PATH" ]; then
|
||||
. $APP_START_SCRIPT_PATH
|
||||
elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then
|
||||
export SHARED_HTPASSWD_PATH
|
||||
export GATEKEEPER_DATAPATH
|
||||
export GATEKEEPER_PORT
|
||||
export LIGHTNING_DATAPATH
|
||||
export BITCOIN_DATAPATH
|
||||
export APP_SCRIPT_PATH
|
||||
export APP_ID
|
||||
export DOCKER_MODE
|
||||
|
||||
if [ "$DOCKER_MODE" = "swarm" ]; then
|
||||
docker stack deploy -c $APP_SCRIPT_PATH/docker-compose.yaml $APP_ID
|
||||
elif [ "$DOCKER_MODE" = "compose" ]; then
|
||||
docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml up -d --remove-orphans
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
<% if (run_as_different_user) { %>
|
||||
OS=$(uname -s)
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
printf "\r\n\033[0;91m'Run as another user' feature is not supported on OSX. User <%= default_username %> will be used to run Cyphernode.\033[0m\r\n\r\n"
|
||||
export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
|
||||
else
|
||||
export USER=$(id -u <%= username %>):$(id -g <%= username %>)
|
||||
fi
|
||||
<% } else { %>
|
||||
export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
|
||||
<% } %>
|
||||
|
||||
export ARCH=$(uname -m)
|
||||
current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)"
|
||||
|
||||
<% if (docker_mode == 'swarm') { %>
|
||||
docker stack deploy -c $current_path/docker-compose.yaml cyphernode
|
||||
<% } else if(docker_mode == 'compose') { %>
|
||||
docker-compose -f $current_path/docker-compose.yaml up -d --remove-orphans
|
||||
<% } %>
|
||||
|
||||
start_apps
|
||||
|
||||
. ./testdeployment.sh
|
||||
59
cyphernodeconf_docker/templates/installer/stop.sh
Normal file
59
cyphernodeconf_docker/templates/installer/stop.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
|
||||
current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)"
|
||||
|
||||
|
||||
# be aware that randomly downloaded cyphernode apps will have access to
|
||||
# your configuration and filesystem.
|
||||
# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!!
|
||||
# TODO: Test if we can mitigate this security issue by
|
||||
# running app dockers inside a docker container
|
||||
|
||||
stop_apps() {
|
||||
local SCRIPT_NAME="stop.sh"
|
||||
local APP_SCRIPT_PATH
|
||||
local APP_START_SCRIPT_PATH
|
||||
local APP_ID
|
||||
|
||||
for i in $current_path/apps/*
|
||||
do
|
||||
APP_SCRIPT_PATH=$(echo $i)
|
||||
if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
|
||||
APP_STOP_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
|
||||
APP_ID=$(basename $APP_SCRIPT_PATH)
|
||||
|
||||
if [ -f "$APP_STOP_SCRIPT_PATH" ]; then
|
||||
. $APP_STOP_SCRIPT_PATH
|
||||
elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then
|
||||
export SHARED_HTPASSWD_PATH
|
||||
export GATEKEEPER_DATAPATH
|
||||
export GATEKEEPER_PORT
|
||||
export LIGHTNING_DATAPATH
|
||||
export BITCOIN_DATAPATH
|
||||
export APP_SCRIPT_PATH
|
||||
export APP_ID
|
||||
export DOCKER_MODE
|
||||
|
||||
if [ "$DOCKER_MODE" = "swarm" ]; then
|
||||
docker stack rm $APP_ID
|
||||
elif [ "$DOCKER_MODE" = "compose" ]; then
|
||||
docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml down
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
. ./.cyphernodeconf/installer/config.sh
|
||||
stop_apps
|
||||
|
||||
<% if (docker_mode == 'swarm') { %>
|
||||
export USER=$(id -u):$(id -g)
|
||||
export ARCH=$(uname -m)
|
||||
docker stack rm cyphernode
|
||||
<% } else if(docker_mode == 'compose') { %>
|
||||
export USER=$(id -u):$(id -g)
|
||||
export ARCH=$(uname -m)
|
||||
docker-compose -f $current_path/docker-compose.yaml down
|
||||
<% } %>
|
||||
@@ -1,9 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./.cyphernodeconf/installer/config.sh
|
||||
|
||||
# be aware that randomly downloaded cyphernode apps will have access to
|
||||
# your configuration and filesystem.
|
||||
# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!!
|
||||
# TODO: Test if we can mitigate this security issue by
|
||||
# running app dockers inside a docker container
|
||||
|
||||
test_apps() {
|
||||
local SCRIPT_NAME="test.sh"
|
||||
local APP_SCRIPT_PATH
|
||||
local APP_START_SCRIPT_PATH
|
||||
local APP_ID
|
||||
local returncode=0
|
||||
|
||||
for i in $current_path/apps/*
|
||||
do
|
||||
APP_SCRIPT_PATH=$(echo $i)
|
||||
if [ -d "$APP_SCRIPT_PATH" ]; then
|
||||
APP_TEST_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
|
||||
|
||||
if [ -f "$APP_TEST_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
|
||||
APP_ID=$(basename "$APP_SCRIPT_PATH")
|
||||
printf "\r\n\e[1;36mTesting $APP_ID... \e[1;0m"
|
||||
. $APP_TEST_SCRIPT_PATH
|
||||
local rc=$?
|
||||
|
||||
if [ ""$rc -eq "0" ]; then
|
||||
printf "\e[1;36m$APP_ID rocks!\e[1;0m"
|
||||
fi
|
||||
returncode=$(($rc | ${returncode}))
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return $returncode
|
||||
}
|
||||
|
||||
<% if (run_as_different_user) { %>
|
||||
OS=$(uname -s)
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
printf "\r\n\033[0;91m'Run as another user' feature is not supported on OSX. User <%= default_username %> will be used to run Cyphernode.\033[0m\r\n\r\n"
|
||||
export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
|
||||
else
|
||||
export USER=$(id -u <%= username %>):$(id -g <%= username %>)
|
||||
@@ -15,12 +52,6 @@ export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
|
||||
export ARCH=$(uname -m)
|
||||
current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)"
|
||||
|
||||
<% if (docker_mode == 'swarm') { %>
|
||||
docker stack deploy -c $current_path/docker-compose.yaml cyphernode
|
||||
<% } else if(docker_mode == 'compose') { %>
|
||||
docker-compose -f $current_path/docker-compose.yaml up -d --remove-orphans
|
||||
<% } %>
|
||||
|
||||
arch=$(uname -m)
|
||||
case "${arch}" in arm*)
|
||||
printf "\r\n\033[1;31mSince we're on a slow RPi, let's give Docker 60 more seconds before performing our tests...\033[0m\r\n\r\n"
|
||||
@@ -32,19 +63,26 @@ esac
|
||||
docker run --rm -it -v $current_path/testfeatures.sh:/testfeatures.sh \
|
||||
-v <%= gatekeeper_datapath %>:/gatekeeper \
|
||||
-v $current_path:/dist \
|
||||
--network cyphernodenet alpine:3.8 /testfeatures.sh
|
||||
-v cyphernode_bitcoin_monitor:/bitcoin_monitor:ro \
|
||||
--network cyphernodenet eclipse-mosquitto:1.6.2 /testfeatures.sh
|
||||
|
||||
if [ -f $current_path/exitStatus.sh ]; then
|
||||
. $current_path/exitStatus.sh
|
||||
rm -f $current_path/exitStatus.sh
|
||||
fi
|
||||
|
||||
test_apps
|
||||
|
||||
EXIT_STATUS=$(($? | ${EXIT_STATUS}))
|
||||
|
||||
printf "\r\n\e[1;32mTests finished.\e[0m\n"
|
||||
|
||||
if [ "$EXIT_STATUS" -ne "0" ]; then
|
||||
printf "\r\n\033[1;31mThere was an error during cyphernode installation. Please see Docker's logs for more information. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m"
|
||||
printf "\r\n\033[1;31mThere was an error during cyphernode installation. Please see Docker's logs for more information. Run ./testdeployment.sh to rerun the tests. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "\r\n\033[0;92mDepending on your current location and DNS settings, point your favorite browser to one of the following URLs to access Cyphernode's status page:\r\n"
|
||||
printf "\r\n"
|
||||
printf "\033[0;95m<% cns.forEach(cn => { %><%= ('https://' + cn + '/status/\\r\\n') %><% }) %>\033[0m\r\n"
|
||||
printf "\033[0;95m<% cns.forEach(cn => { %><%= ('https://' + cn + '/welcome\\r\\n') %><% }) %>\033[0m\r\n"
|
||||
printf "\033[0;92mUse 'admin' as the username with the configuration password you selected at the beginning of the configuration process.\r\n\r\n\033[0m"
|
||||
@@ -24,7 +24,7 @@ checkgatekeeper() {
|
||||
sleep 2
|
||||
|
||||
echo " Testing expired request... " > /dev/console
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo)
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getblockinfo)
|
||||
[ "${rc}" -ne "403" ] && return 10
|
||||
|
||||
# Let's test authentication (signature)
|
||||
@@ -34,7 +34,7 @@ checkgatekeeper() {
|
||||
token="$h64.$p64.a$s"
|
||||
|
||||
echo " Testing bad signature... " > /dev/console
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo)
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getblockinfo)
|
||||
[ "${rc}" -ne "403" ] && return 30
|
||||
|
||||
# Let's test authorization (action access for groups)
|
||||
@@ -42,7 +42,7 @@ checkgatekeeper() {
|
||||
token="$h64.$p64.$s"
|
||||
|
||||
echo " Testing watcher trying to do a spender action... " > /dev/console
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getbalance)
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getbalance)
|
||||
[ "${rc}" -ne "403" ] && return 40
|
||||
|
||||
id="002"
|
||||
@@ -52,7 +52,7 @@ checkgatekeeper() {
|
||||
token="$h64.$p64.$s"
|
||||
|
||||
echo " Testing spender trying to do an internal action call... " > /dev/console
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf)
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/conf)
|
||||
[ "${rc}" -ne "403" ] && return 50
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ checkgatekeeper() {
|
||||
token="$h64.$p64.$s"
|
||||
|
||||
echo " Testing admin trying to do an internal action call... " > /dev/console
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf)
|
||||
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/conf)
|
||||
[ "${rc}" -ne "403" ] && return 60
|
||||
|
||||
echo -e "\e[1;36mGatekeeper rocks!" > /dev/console
|
||||
@@ -83,6 +83,34 @@ checkpycoin() {
|
||||
return 0
|
||||
}
|
||||
|
||||
checkbroker() {
|
||||
echo -en "\r\n\e[1;36mTesting Broker... " > /dev/console
|
||||
local rc
|
||||
|
||||
rc=$(mosquitto_pub -h broker -t "testtopic" -m "testbroker")
|
||||
[ "$?" -ne "0" ] && return 110
|
||||
|
||||
echo -e "\e[1;36mBroker rocks!" > /dev/console
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checknotifier() {
|
||||
echo -en "\r\n\e[1;36mTesting Notifier... " > /dev/console
|
||||
local response
|
||||
local returncode
|
||||
|
||||
response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"web\",\"url\":\"http://proxy:8888/helloworld\"}")
|
||||
returncode=$?
|
||||
[ "${returncode}" -ne "0" ] && return 115
|
||||
http_code=$(echo "${response}" | jq -r ".http_code")
|
||||
[ "${http_code}" -ge "400" ] && return 118
|
||||
|
||||
echo -e "\e[1;36mNotifier rocks!" > /dev/console
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checkots() {
|
||||
echo -en "\r\n\e[1;36mTesting OTSclient... " > /dev/console
|
||||
local rc
|
||||
@@ -121,8 +149,8 @@ checklnnode() {
|
||||
}
|
||||
|
||||
checkservice() {
|
||||
local interval=10
|
||||
local totaltime=120
|
||||
local interval=15
|
||||
local totaltime=180
|
||||
local outcome
|
||||
local returncode=0
|
||||
local endtime=$(($(date +%s) + ${totaltime}))
|
||||
@@ -133,12 +161,12 @@ checkservice() {
|
||||
while :
|
||||
do
|
||||
outcome=0
|
||||
for container in gatekeeper proxy proxycron pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
echo -e " \e[0;32mVerifying \e[0;33m${container}\e[0;32m..." > /dev/console
|
||||
(ping -c 10 ${container} 2> /dev/null | grep "0% packet loss" > /dev/null) &
|
||||
eval ${container}=$!
|
||||
done
|
||||
for container in gatekeeper proxy proxycron pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
eval wait '$'${container} ; returncode=$? ; outcome=$((${outcome} + ${returncode}))
|
||||
eval c_${container}=${returncode}
|
||||
done
|
||||
@@ -158,9 +186,9 @@ checkservice() {
|
||||
# { "name": "pycoin", "active":true },
|
||||
# { "name": "otsclient", "active":true },
|
||||
# { "name": "bitcoin", "active":true },
|
||||
# { "name": "lightning", "active":true }
|
||||
# { "name": "lightning", "active":true },
|
||||
# ]
|
||||
for container in gatekeeper proxy proxycron pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
|
||||
[ -n "${result}" ] && result="${result},"
|
||||
result="${result}{\"name\":\"${container}\",\"active\":"
|
||||
eval "returncode=\$c_${container}"
|
||||
@@ -179,8 +207,8 @@ checkservice() {
|
||||
}
|
||||
|
||||
timeout_feature() {
|
||||
local interval=10
|
||||
local totaltime=60
|
||||
local interval=15
|
||||
local totaltime=120
|
||||
local testwhat=${1}
|
||||
local returncode
|
||||
local endtime=$(($(date +%s) + ${totaltime}))
|
||||
@@ -218,14 +246,14 @@ feature_status() {
|
||||
# { "name": "pycoin", "active":true },
|
||||
# { "name": "otsclient", "active":true },
|
||||
# { "name": "bitcoin", "active":true },
|
||||
# { "name": "lightning", "active":true }
|
||||
# { "name": "lightning", "active":true },
|
||||
# ],
|
||||
# "features": [
|
||||
# { "name": "gatekeeper", "working":true },
|
||||
# { "name": "pycoin", "working":true },
|
||||
# { "name": "otsclient", "working":true },
|
||||
# { "name": "bitcoin", "working":true },
|
||||
# { "name": "lightning", "working":true }
|
||||
# { "name": "lightning", "working":true },
|
||||
# ]
|
||||
#}
|
||||
|
||||
@@ -233,7 +261,11 @@ feature_status() {
|
||||
|
||||
echo "EXIT_STATUS=1" > /dist/exitStatus.sh
|
||||
|
||||
brokenproxy="false"
|
||||
#############################
|
||||
# Ping containers and PROXY #
|
||||
#############################
|
||||
|
||||
workingproxy="true"
|
||||
containers=$(checkservice)
|
||||
returncode=$?
|
||||
finalreturncode=${returncode}
|
||||
@@ -242,7 +274,7 @@ if [ "${returncode}" -ne "0" ]; then
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"proxy\") | .active")
|
||||
if [ "${status}" = "false" ]; then
|
||||
echo -e "\e[1;31mThe Proxy, the main Cyphernode's component, is not responding. We will only test the gatekeeper if its container is up, but you'll see errors for the other components. Please check the logs." > /dev/console
|
||||
brokenproxy="true"
|
||||
workingproxy="false"
|
||||
fi
|
||||
else
|
||||
echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thourough tests..." > /dev/console
|
||||
@@ -254,10 +286,14 @@ fi
|
||||
# { "name": "pycoin", "working":true },
|
||||
# { "name": "otsclient", "working":true },
|
||||
# { "name": "bitcoin", "working":true },
|
||||
# { "name": "lightning", "working":true }
|
||||
# { "name": "lightning", "working":true },
|
||||
# ]
|
||||
|
||||
result="${containers},\"features\":[{\"name\":\"gatekeeper\",\"working\":"
|
||||
#############################
|
||||
# GATEKEEPER #
|
||||
#############################
|
||||
|
||||
result="${containers},\"features\":[{\"coreFeature\":true, \"name\":\"proxy\",\"working\":${workingproxy}}, {\"coreFeature\":true, \"name\":\"gatekeeper\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"gatekeeper\") | .active")
|
||||
if [ "${status}" = "true" ]; then
|
||||
timeout_feature checkgatekeeper
|
||||
@@ -268,9 +304,43 @@ fi
|
||||
finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Gatekeeper error!')}"
|
||||
|
||||
result="${result},{\"name\":\"pycoin\",\"working\":"
|
||||
#############################
|
||||
# BROKER #
|
||||
#############################
|
||||
|
||||
result="${result},{\"coreFeature\":true, \"name\":\"broker\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"broker\") | .active")
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checkbroker
|
||||
returncode=$?
|
||||
else
|
||||
returncode=1
|
||||
fi
|
||||
finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Broker error!')}"
|
||||
|
||||
#############################
|
||||
# NOTIFIER #
|
||||
#############################
|
||||
|
||||
result="${result},{\"coreFeature\":true, \"name\":\"notifier\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"notifier\") | .active")
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checknotifier
|
||||
returncode=$?
|
||||
else
|
||||
returncode=1
|
||||
fi
|
||||
finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Notifier error!')}"
|
||||
|
||||
#############################
|
||||
# PYCOIN #
|
||||
#############################
|
||||
|
||||
result="${result},{\"coreFeature\":true, \"name\":\"pycoin\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"pycoin\") | .active")
|
||||
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checkpycoin
|
||||
returncode=$?
|
||||
else
|
||||
@@ -280,9 +350,13 @@ finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Pycoin error!')}"
|
||||
|
||||
<% if (features.indexOf('otsclient') != -1) { %>
|
||||
result="${result},{\"name\":\"otsclient\",\"working\":"
|
||||
#############################
|
||||
# OTSCLIENT #
|
||||
#############################
|
||||
|
||||
result="${result},{\"coreFeature\":false, \"name\":\"otsclient\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"otsclient\") | .active")
|
||||
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checkots
|
||||
returncode=$?
|
||||
else
|
||||
@@ -292,9 +366,16 @@ finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'OTSclient error!')}"
|
||||
<% } %>
|
||||
|
||||
result="${result},{\"name\":\"bitcoin\",\"working\":"
|
||||
#############################
|
||||
# BITCOIN #
|
||||
#############################
|
||||
|
||||
echo -e "\r\n\e[1;36mWaiting for Bitcoin Core to be ready... " > /dev/console
|
||||
timeout_feature '[ -f "/bitcoin_monitor/up" ]'
|
||||
|
||||
result="${result},{\"coreFeature\":true, \"name\":\"bitcoin\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"bitcoin\") | .active")
|
||||
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checkbitcoinnode
|
||||
returncode=$?
|
||||
else
|
||||
@@ -304,9 +385,13 @@ finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Bitcoin error!')}"
|
||||
|
||||
<% if (features.indexOf('lightning') != -1) { %>
|
||||
result="${result},{\"name\":\"lightning\",\"working\":"
|
||||
#############################
|
||||
# LIGHTNING #
|
||||
#############################
|
||||
|
||||
result="${result},{\"coreFeature\":false, \"name\":\"lightning\",\"working\":"
|
||||
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"lightning\") | .active")
|
||||
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
|
||||
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
|
||||
timeout_feature checklnnode
|
||||
returncode=$?
|
||||
else
|
||||
@@ -314,12 +399,11 @@ else
|
||||
fi
|
||||
finalreturncode=$((${returncode} | ${finalreturncode}))
|
||||
result="${result}$(feature_status ${returncode} 'Lightning error!')}"
|
||||
|
||||
<% } %>
|
||||
|
||||
result="{${result}]}"
|
||||
|
||||
echo "${result}" > /gatekeeper/installation.json
|
||||
|
||||
echo -e "\r\n\e[1;32mTests finished.\e[0m" > /dev/console
|
||||
|
||||
echo "EXIT_STATUS=${finalreturncode}" > /dist/exitStatus.sh
|
||||
@@ -0,0 +1,5 @@
|
||||
# How to create the hmac for the cookie file:
|
||||
|
||||
```
|
||||
# echo -n "access-key" | openssl dgst -hmac "cyphernode:sparkwallet" -sha256 -binary | base64 | sed 's/[\+\W]//g'
|
||||
```
|
||||
@@ -1,4 +1,7 @@
|
||||
<% if (net === 'testnet') { %>
|
||||
<% if (net === 'regtest') { %>
|
||||
# regtest
|
||||
network=regtest
|
||||
<% } else if (net === 'testnet') { %>
|
||||
# testnet
|
||||
network=testnet
|
||||
<% } else if (net === 'mainnet') { %>
|
||||
@@ -10,6 +13,9 @@ alias=<%= lightning_nodename %>
|
||||
<% if( lightning_nodecolor ) { %>
|
||||
rgb=<%= lightning_nodecolor %>
|
||||
<% } %>
|
||||
bitcoin-rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>
|
||||
bitcoin-rpcuser=<%= bitcoin_rpcuser %>
|
||||
bitcoin-rpcpassword=<%= bitcoin_rpcpassword %>
|
||||
|
||||
addr=0.0.0.0:9735
|
||||
|
||||
<% if( locals.lightning_external_ip ) { %>
|
||||
announce-addr=<%= locals.lightning_external_ip %>:9735
|
||||
<% } %>
|
||||
@@ -0,0 +1 @@
|
||||
cyphernode:sparkwallet:FoeDdQw5yl7pPfqdlGy3OEk/txGqyJjSbVtffhzs7kc=
|
||||
1
cyphernodeconf_docker/templates/traefik/acme.json
Normal file
1
cyphernodeconf_docker/templates/traefik/acme.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
cyphernodeconf_docker/templates/traefik/htpasswd
Normal file
1
cyphernodeconf_docker/templates/traefik/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
admin:<%- adminhash %>
|
||||
31
cyphernodeconf_docker/templates/traefik/traefik.toml
Normal file
31
cyphernodeconf_docker/templates/traefik/traefik.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
debug = false
|
||||
|
||||
logLevel = "ERROR"
|
||||
defaultEntryPoints = ["https","http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":<%= traefik_http_port %>"
|
||||
[entryPoints.http.redirect]
|
||||
entryPoint = "https"
|
||||
[entryPoints.https]
|
||||
address = ":<%= traefik_https_port %>"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[retry]
|
||||
|
||||
[docker]
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
domain = "cyphernode.localhost"
|
||||
watch = true
|
||||
exposedByDefault = false
|
||||
|
||||
[acme]
|
||||
email = "letsencrypt@yourdomain.com"
|
||||
storage = "acme.json"
|
||||
entryPoint = "https"
|
||||
onHostRule = true
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
[[acme.domains]]
|
||||
main = "cyphernode.yourdomain.com"
|
||||
42
cyphernodeconf_docker/test/apikey.test.js
Normal file
42
cyphernodeconf_docker/test/apikey.test.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const ApiKey = require('../lib/apikey.js');
|
||||
|
||||
|
||||
test( 'Create ApiKey instance', ()=>{
|
||||
const apiKey = new ApiKey('testId',['group1','group2']);
|
||||
expect( apiKey ).not.toBe( undefined );
|
||||
expect( apiKey.id ).toEqual( 'testId' );
|
||||
expect( apiKey.groups ).toEqual( ['group1','group2'] );
|
||||
expect( apiKey.key ).toBe( undefined );
|
||||
expect( apiKey.script ).toEqual( 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' );
|
||||
});
|
||||
|
||||
test( 'Create ApiKey instance and randomise it', async ()=>{
|
||||
const apiKey = new ApiKey('testId',['group1','group2']);
|
||||
await apiKey.randomiseKey();
|
||||
expect( apiKey ).not.toBe( undefined );
|
||||
expect( apiKey.id ).toEqual( 'testId' );
|
||||
expect( apiKey.groups ).toEqual( ['group1','group2'] );
|
||||
expect( apiKey.key ).not.toBe( undefined );
|
||||
expect( apiKey.script ).toEqual( 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' );
|
||||
});
|
||||
|
||||
test( 'Create ApiKey instance, randomise it and use getters', async ()=>{
|
||||
const apiKey = new ApiKey('testId',['group1','group2']);
|
||||
await apiKey.randomiseKey();
|
||||
const keyString = apiKey.getKey();
|
||||
const script = apiKey.script;
|
||||
expect( keyString ).not.toBe( undefined );
|
||||
expect( apiKey.id ).toEqual( 'testId' );
|
||||
expect( apiKey.getClientInformation() ).toEqual( 'testId='+keyString );
|
||||
expect( apiKey.getConfigEntry() ).toEqual( `kapi_id="testId";kapi_key="${keyString}";kapi_groups="group1,group2";${script}` );
|
||||
});
|
||||
|
||||
test( 'Set properties of ApiKey instance from config entry', async () => {
|
||||
const configEntry = 'kapi_id="000";kapi_key="b1fdc782037609f8ecc063ac192e92d57544263a950c637ed6b7d79cc9eb9f95";kapi_groups="stats";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}';
|
||||
const apiKey = new ApiKey();
|
||||
apiKey.setFromConfigEntry(configEntry);
|
||||
expect( apiKey.id ).toEqual('000');
|
||||
expect( apiKey.groups ).toEqual(['stats']);
|
||||
expect( apiKey.key ).toEqual('b1fdc782037609f8ecc063ac192e92d57544263a950c637ed6b7d79cc9eb9f95');
|
||||
expect( apiKey.script ).toEqual('eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}')
|
||||
})
|
||||
26
cyphernodeconf_docker/test/archive.test.js
Normal file
26
cyphernodeconf_docker/test/archive.test.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const Archive = require('../lib/archive.js');
|
||||
const tmp = require('tmp');
|
||||
const path = require('path');
|
||||
|
||||
test( 'Create Archive instance', ()=>{
|
||||
new Archive( '/tmp/testArchive.7z', 'test123' );
|
||||
});
|
||||
|
||||
test( 'Write, Read, Delete', async ()=>{
|
||||
const tmpDir = tmp.dirSync();
|
||||
const archive = new Archive( path.join(tmpDir.name,'archive.7z'), 'test123' );
|
||||
|
||||
await archive.writeEntry('testEntry', 'testContent' );
|
||||
|
||||
const c0 = await archive.readEntry( 'testEntry' );
|
||||
|
||||
expect( c0.value ).toEqual( 'testContent' );
|
||||
|
||||
await archive.deleteEntry('testEntry');
|
||||
|
||||
const c1 = await archive.readEntry( 'testEntry' );
|
||||
|
||||
expect( c1.value ).toBe( '' );
|
||||
|
||||
tmpDir.removeCallback();
|
||||
});
|
||||
60
cyphernodeconf_docker/test/cert.test.js
Normal file
60
cyphernodeconf_docker/test/cert.test.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const Cert = require('../lib/cert.js');
|
||||
|
||||
test( 'Create Cert instance', ()=>{
|
||||
const cert = new Cert();
|
||||
expect( cert.args.days ).toBe( 3650 );
|
||||
});
|
||||
|
||||
test( 'buildConfig', ()=>{
|
||||
const cert = new Cert();
|
||||
const conf = cert.buildConfig(['127.0.0.1','localhost','gatekeeper']);
|
||||
expect( conf ).toEqual(`
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
x509_extensions = v3_ca
|
||||
prompt = no
|
||||
[req_distinguished_name]
|
||||
CN = localhost
|
||||
[v3_ca]
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
DNS.2 = gatekeeper
|
||||
IP.1 = 127.0.0.1
|
||||
`);
|
||||
});
|
||||
|
||||
|
||||
test( 'cns', () => {
|
||||
const cert = new Cert();
|
||||
const cns = cert.cns(' abc, cde' );
|
||||
expect( cns ).toEqual([
|
||||
'127.0.0.1',
|
||||
'localhost',
|
||||
'gatekeeper',
|
||||
'abc',
|
||||
'cde'
|
||||
]);
|
||||
});
|
||||
|
||||
test( 'create', async ()=>{
|
||||
jest.setTimeout(999999);
|
||||
const cert = new Cert();
|
||||
const cns = cert.cns('abc,cde' );
|
||||
const r = await cert.create( cns );
|
||||
|
||||
expect( r.code ).toBe(0);
|
||||
expect( r.key ).not.toBe(undefined);
|
||||
expect( r.cert ).not.toBe(undefined);
|
||||
});
|
||||
|
||||
test( 'create throws', async ()=>{
|
||||
const cert = new Cert();
|
||||
let err;
|
||||
try {
|
||||
await cert.create();
|
||||
} catch( e ) {
|
||||
err = e;
|
||||
}
|
||||
expect( err ).not.toBe(undefined);
|
||||
});
|
||||
151
cyphernodeconf_docker/test/config.test.js
Normal file
151
cyphernodeconf_docker/test/config.test.js
Normal file
@@ -0,0 +1,151 @@
|
||||
const Config = require('../lib/config.js');
|
||||
const ApiKey = require('../lib/apikey.js');
|
||||
const fs = require('fs');
|
||||
const { promisify } = require( 'util' );
|
||||
const existsAsync = promisify( fs.exists );
|
||||
const configV010 = require('./data/config.0.1.0.json');
|
||||
const configV020 = require('./data/config.0.2.0.json');
|
||||
const configV022 = require('./data/config.0.2.2.json');
|
||||
|
||||
|
||||
|
||||
const expect020 = async (data) => {
|
||||
const gatekeeper_keys = JSON.parse(JSON.stringify(configV010.gatekeeper_keys));
|
||||
expect( data.gatekeeper_keys.configEntries.length ).toBe( 4 );
|
||||
for( let i=0; i<3; i++ ) {
|
||||
const configEntry = data.gatekeeper_keys.configEntries[i+1];
|
||||
const oldConfigEntry = gatekeeper_keys.configEntries[i];
|
||||
|
||||
const key = new ApiKey();
|
||||
key.setFromConfigEntry( configEntry )
|
||||
|
||||
const oldKey = new ApiKey();
|
||||
oldKey.setFromConfigEntry( oldConfigEntry );
|
||||
|
||||
expect( key.id ).toEqual( oldKey.id );
|
||||
expect( key.key ).toEqual( oldKey.key );
|
||||
expect( key.script ).toEqual( oldKey.script );
|
||||
|
||||
for( let oldGroup of oldKey.groups ) {
|
||||
expect( key.groups ).toContain(oldGroup);
|
||||
}
|
||||
|
||||
expect( key.groups ).toContain('stats');
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const expect022 = async (data) => {
|
||||
expect( data.lightning_announce ).not.toBe(undefined);
|
||||
expect( data.gatekeeper_expose ).not.toBe(undefined);
|
||||
};
|
||||
|
||||
let configFileName;
|
||||
|
||||
beforeAll(() => {
|
||||
configFileName = '/tmp/config'+Math.round(Math.random()*100000000)+'.7z';
|
||||
});
|
||||
|
||||
test( 'create config v0.1.0', () => {
|
||||
new Config(configV010);
|
||||
});
|
||||
|
||||
|
||||
test( 'create config v0.2.0', () => {
|
||||
new Config(configV020);
|
||||
});
|
||||
|
||||
|
||||
test( 'validate config v0.1.0', () => {
|
||||
const config = new Config(JSON.parse(JSON.stringify(configV010)));
|
||||
config.data.foo = "bar";
|
||||
config.data.bar = "foo";
|
||||
config.validate();
|
||||
expect( config.data.foo ).toBe( undefined );
|
||||
expect( config.data.bar ).toBe( undefined );
|
||||
});
|
||||
|
||||
|
||||
test( 'validate config v0.2.0', () => {
|
||||
const config = new Config(JSON.parse(JSON.stringify(configV020)));
|
||||
config.data.foo = "bar";
|
||||
config.data.bar = "foo";
|
||||
config.validate();
|
||||
expect( config.data.foo ).toBe( undefined );
|
||||
expect( config.data.bar ).toBe( undefined );
|
||||
});
|
||||
|
||||
test( 'validate config v0.2.2', () => {
|
||||
const config = new Config(JSON.parse(JSON.stringify(configV022)));
|
||||
config.data.foo = "bar";
|
||||
config.data.bar = "foo";
|
||||
config.validate();
|
||||
expect( config.data.foo ).toBe( undefined );
|
||||
expect( config.data.bar ).toBe( undefined );
|
||||
});
|
||||
|
||||
test( 'generateMigrationPathToLatest from 0.1.0', async () => {
|
||||
const config = new Config();
|
||||
const path = config.generateMigrationPathToLatest('0.1.0');
|
||||
expect( path ).toEqual( [config.migrate_0_1_0_to_0_2_0, config.migrate_0_2_0_to_0_2_2] );
|
||||
});
|
||||
|
||||
test( 'generateMigrationPathToLatest from 0.2.0', async () => {
|
||||
const config = new Config();
|
||||
const path = config.generateMigrationPathToLatest('0.2.0');
|
||||
expect( path ).toEqual( [config.migrate_0_2_0_to_0_2_2] );
|
||||
});
|
||||
|
||||
test( 'generateMigrationPathToLatest from 0.2.2', async () => {
|
||||
const config = new Config();
|
||||
const path = config.generateMigrationPathToLatest('0.2.2');
|
||||
expect( path ).toBe( undefined );
|
||||
});
|
||||
|
||||
test( 'migrate 0.1.0 -> 0.2.0', async () => {
|
||||
const config = new Config();
|
||||
config.setData( JSON.parse(JSON.stringify(configV010)) );
|
||||
// deep clone gatekeeper_keys
|
||||
await config.migrate_0_1_0_to_0_2_0();
|
||||
expect020( config.data );
|
||||
|
||||
});
|
||||
|
||||
test( 'migrate 0.2.0 -> 0.2.2', async () => {
|
||||
const config = new Config();
|
||||
config.setData( JSON.parse(JSON.stringify(configV020)) );
|
||||
await config.migrate_0_2_0_to_0_2_2();
|
||||
expect022(config.data);
|
||||
|
||||
});
|
||||
|
||||
test( 'migrateFrom 0.1.0', async () => {
|
||||
const config = new Config();
|
||||
config.setData( JSON.parse(JSON.stringify(configV010)) );
|
||||
await config.migrateFrom('0.1.0');
|
||||
config.validate();
|
||||
expect020(config.data);
|
||||
expect022(config.data);
|
||||
expect( config.data.traefik_http_port ).toEqual( 80 );
|
||||
expect( config.data.traefik_https_port ).toEqual( 443 );
|
||||
});
|
||||
|
||||
|
||||
|
||||
test( 'serialise', async () => {
|
||||
const config = new Config();
|
||||
config.setData( JSON.parse(JSON.stringify(configV022)) )
|
||||
const success = await config.serialize(configFileName,'test123' );
|
||||
const exists = await existsAsync(configFileName);
|
||||
expect( success ).toEqual( true );
|
||||
expect( exists ).toEqual( true );
|
||||
});
|
||||
|
||||
test( 'deserialise', async () => {
|
||||
const config = new Config( {
|
||||
setup_version: 'setup_version'
|
||||
} );
|
||||
await config.deserialize(configFileName,'test123' );
|
||||
expect( config.data ).toEqual( configV022 );
|
||||
});
|
||||
|
||||
55
cyphernodeconf_docker/test/data/config.0.1.0.json
Normal file
55
cyphernodeconf_docker/test/data/config.0.1.0.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"features": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"net": "testnet",
|
||||
"use_xpub": true,
|
||||
"installer_mode": "docker",
|
||||
"run_as_different_user": true,
|
||||
"username": "cyphernode",
|
||||
"docker_mode": "compose",
|
||||
"bitcoin_rpcuser": "bitcoin",
|
||||
"bitcoin_rpcpassword": "test123",
|
||||
"bitcoin_uacomment": "",
|
||||
"bitcoin_prune": false,
|
||||
"bitcoin_prune_size": 550,
|
||||
"bitcoin_node_ip": "123.123.123.123",
|
||||
"bitcoin_datapath": "/Users/jash/.cyphernode/bitcoin",
|
||||
"bitcoin_mode": "internal",
|
||||
"bitcoin_expose": false,
|
||||
"lightning_expose": true,
|
||||
"gatekeeper_apiproperties": "GKAP",
|
||||
"gatekeeper_keys": {
|
||||
"configEntries": [
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
],
|
||||
"clientInformation": [
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
},
|
||||
"gatekeeper_sslcert": "-----BEGIN CERTIFICATE-----\nMIIE/jCCAuagAwIBAgIJAIBv4aiI2NRtMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV\nBAMME2Rpc2swYm9vay5mcml0ei5ib3gwHhcNMTkwMTE3MTcwMDA5WhcNMjkwMTE0\nMTcwMDA5WjAeMRwwGgYDVQQDDBNkaXNrMGJvb2suZnJpdHouYm94MIICIjANBgkq\nhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyVKNTGlPfd4QX9HaDc9a6prbc9il4jtR\niChSlMf3/6UfAMcS+xVgR2iR8FK/DQuFzxn+6BybpoiD333rjDr7zR7y9px3Xph2\nbmsjZy0hv9SIBbx0DJvvwODTlWTAH8qgU2DN6xWc7vjgeGi5uTpnmwWrkH6BjtVr\nwoBkF0JmfH7KiLS/QjWqPKeI6o/GpvCP9meD131Sq/ReoOTrJ4F5aNdhAril4nU5\n6e7Y+Iyp35DZSLuU+pDJAhxEvkYGas1ted5RRxlho8ukaoABCbmaTeNmgsJxK2SC\nABjfUc38aAlNLuMbMMR7Q85Z84OTJiUqanVczwdSj1QHlNCWZK1McBPhj2m2Wdge\ngedrq5XcjQGChzTEozcFntU0qzY3ja1+DOE8UaMaTrDH4saUXCMZk3W1m5mmiZW3\nmcB0cKGdeg6K6USg1BwBTU9qolUusxz5T0tNxjcMlXU93P17d4s5IXfliXhMNr/6\n4fl78Ey3FNprTix4alW7hBAp/eA/LhS55s3jwdoVzJl4RELC0284pahj5exYQwU6\nzjLedMxzC+7veQYwWfZOs9jVCTP0YStuT0j9xD3ausLZyB1Egbsajyy71IeoYOf1\n9S6dFIXE5LHAw2j3D3bh5wb019I8V5szGbeemdBpb3m+bzT8qjLSNranuT41CIHd\nIYjq85vDEJsCAwEAAaM/MD0wOwYDVR0RBDQwMoITZGlzazBib29rLmZyaXR6LmJv\neIIJbG9jYWxob3N0ggpnYXRla2VlcGVyhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC\nAQBrE4bJsIMwSRPng94PcqR5F6Cux0bkwezALJCHpjHTuqok/wHHE5dZsAXcSsYc\n5givuBESih6CpY5h21Od0TBugyv3FCRY8OoaBXtlO6FYlEnVeJ8AOexJTb3qcbBS\nHU8MBWEydUh5HFA3PRKAG0Y4cvUK4WXJZ42Et3td0NkGFOv6bxdtVGB4Vz7FGn+3\nqd9fpmFCdQYDp6RSZDDz4B8XLsVuTeTES5GbUMSQAGanP7jxMr04wQ3MuoZrRODN\nFatifOJfq0fZddsBjJbrTLxArIqaPh3J4xzwiNE5du4CQDQrbbHXG22kuvbr5foA\ncixLnuyWMq0a5a70mSNS6TZ3nq4ATXNNa0cZ8fBxHqHGTLM8gQisW8vTaZfIFh/i\nhnFcGxtpo1ryi7JG9HCWsh0x20677iag5MuZfv2s4TbK71Ol6WV4FravCqU0qgbn\nTTl+BnYw3H67FO/a6RD4ISlFWK+8EVEQdMgvPoRuw323YznT0Nd8Q/Gq8raYF2wa\nz9T9OXu6TcVGtfPAgX+AM/+hDqWGxyiFR9ZtLpGOHGP8f+TZA5uCawc8Zry4yN6L\nE0yPIx96pJz59T3k8XbRHTQCaPsSUGRAZIY9LpJj0fIG7zCr9eCBpp2qyzmpyNfx\negN3ILYy1Y8JbJj73HWyP0F3Am7i76tkCWB7tQeFOb5FMg==\n-----END CERTIFICATE-----\n",
|
||||
"gatekeeper_sslkey": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJUo1MaU993hBf\n0doNz1rqmttz2KXiO1GIKFKUx/f/pR8AxxL7FWBHaJHwUr8NC4XPGf7oHJumiIPf\nfeuMOvvNHvL2nHdemHZuayNnLSG/1IgFvHQMm+/A4NOVZMAfyqBTYM3rFZzu+OB4\naLm5OmebBauQfoGO1WvCgGQXQmZ8fsqItL9CNao8p4jqj8am8I/2Z4PXfVKr9F6g\n5OsngXlo12ECuKXidTnp7tj4jKnfkNlIu5T6kMkCHES+RgZqzW153lFHGWGjy6Rq\ngAEJuZpN42aCwnErZIIAGN9RzfxoCU0u4xswxHtDzlnzg5MmJSpqdVzPB1KPVAeU\n0JZkrUxwE+GPabZZ2B6B52urldyNAYKHNMSjNwWe1TSrNjeNrX4M4TxRoxpOsMfi\nxpRcIxmTdbWbmaaJlbeZwHRwoZ16DorpRKDUHAFNT2qiVS6zHPlPS03GNwyVdT3c\n/Xt3izkhd+WJeEw2v/rh+XvwTLcU2mtOLHhqVbuEECn94D8uFLnmzePB2hXMmXhE\nQsLTbzilqGPl7FhDBTrOMt50zHML7u95BjBZ9k6z2NUJM/RhK25PSP3EPdq6wtnI\nHUSBuxqPLLvUh6hg5/X1Lp0UhcTkscDDaPcPduHnBvTX0jxXmzMZt56Z0Glveb5v\nNPyqMtI2tqe5PjUIgd0hiOrzm8MQmwIDAQABAoICAQCI5uA7M+ngd9++qR+VAIqc\nus28y3iSjS/2XSU7E3irmYepqbZYk8KzDIMhX8OXhVxq5wyWns2hw3eZxTEmXP3a\nEM+7r87kvtzaXXTntqMapdYRwINSB8BT8w8uqiKT++Bmko+06y+auhc7Ckwxj2vg\n2Uw/qCdGEA+FZnWp83dp9XaY3ACrb37iXDMY/shhwXjEYMQhB5HuaPDojIL0jHEZ\nQE0x4oq7omfNkqRs8IqcAw4fDaBTe52VF9APa+L1QdjOZMX0iWgCUHrwCTere1FY\n4ehVxw/aKDDXDBLguCiKPrkDx2A4G4SPKYW1uKWZ7PAZENIZ3qrf2I6HPgjnUYmG\nAHQiR3JcwsXFZZAMW/kbqzRCS7CrvNnrzcUL9JAlpFmMDeAFIlbVkFED+kOtVioR\nPAcDWKtlWOWbX3Kn218FCblH86XdzB9H/pgbHxf8cXFcnaqVApxC1zv+uIaV526p\nU9maF8CMVX5bZ21e+dpP1BQ3DDRn3DCQno/QrGGMxK66EBVrHrllHBYpUppcj4w8\nCn5RDhp2KJjYsgX9zjuXif1gdP2jqBCDWCog+YwsoQP1Qp613D6rl5TKaa/rfZmX\nfG5Q98/wfAHwLjIDfwCXWqKOFoGdMzxg9hxk0bSNn64m1UY6OYB4yJs/o6spqGXq\nZRaX2LStSq5fhvB+tjl3AQKCAQEA7wBS3t2dHOVLZGVycxSq1LRoeW44KxZaNSiv\nXj5Xrw/jAnnAEAcVFrCGFY40MC4SNM//VUWHs4zKXxAhK/vFOgSvmnNh6mWDd/sO\nFyzo17fhwhi4u8BIjvchHgwg5aMi4uCLFM8RBZjI5MpMyNBwxT5VLKKKv3N9YtUK\n5JPhXkZJnhOhKj3vTaCeJFBMvcknoqqNcUcEEMo0d+YqUAYh9+8qpjBSKBV+8/VZ\nbQwyjd+EN5ajqRLIjKOy1GrbRwBcxadGdZzqMDYlSVBdAxAssPiUqhTLeHAuGfbK\nB0iF0DLgwl0N/6qx0WtLTA3MY7NcQJ/cjUioJKqbnvouEP2f2wKCAQEA16Qulg5I\nyDbJsiFzmeLNpKSXeh9y1q/hvqizd8R9GAUh9TVSgAnoiCpucguvMYBsXeGU3srr\no9AvxmkmrMWVNZbolaMRv0p+nXPR6uF5tFQJ/jYm1H/jI3ieF2ZXJveQBkqjRsOD\nsWI4HNuarGnsOo8rqV0ybYGFks1dhuvBZp7RemZbXqaFnk/D8FtkmHRsWUdEQifd\njHCATRbUHjAm8tk7HjdJbhYIMCZkbN1HRkx044pk+os37Eqi4Ok+s/MbQ7g2lY+R\nt1sxrJo4dsqxwflz9U6U5ECwH2hxuzpowGXwfcqtPLvVJT1p+TUxIHNb2eUvJg7R\ntsx4BzC4FbaOQQKCAQBMaHYV+hO4oTWxKx2j3P+gcOzVpX8fh03foNov7w5pUVLp\n/7J/bUQ4tMapLYVRwejgKX8f69KufFxWd/mi7iLnoYfigPDU1w9o1EJ09k6aaJcs\nTmsA20BXNHrJ+GasA7OrhM3yISD4ARh4zJQvvzPOW1cvpedlva5gYmvRF9X9Jctz\nViM4NgTDdI4aXfGq2xxozM3bYTDTjVGo41SzsMI7WaLw7pHVbsnfhJjKXBPtd3/1\npZs8+lxTWiJZ9q+Ty3HdXtUP1NKqByV0gtS5nforuc0Ncwh5wKN8eYGtQmpFXX6t\npMJM8m1W3k58Sg0F8tmTb4g7Qvc+gayu7py7odnxAoIBAQC5pJqGjF2UH7acJ7hB\nrsOjDh9p/1D6Cgip/soiPYID/8cwNmuD1wPc1cqnW+/DCfBBEkb7Vm5uZHf8s+Gb\ns620qOoqiGxq5lMCcgcx0lLYL9E6jJv5LO/6RYi0VGKLJW1UPUU7Um45c3kjPtt1\nuuqnr0HDWHxlUbAQpcPyt1uUYP2uBhh675jwpXLlpYiAxxnP8k6NNYzBrsiFlTWf\nl4ywXNtMNAR/RXBfI24pWoZVutSWXzp3hwrp3YdDYQmeGZhLQHedYi/sThIBqfMa\nMX2+pGZztObxac81+tCOgsZTfG8BnE5vjrT8jlaBOI3Ghgl5GJjyhqd8W6KpbgPM\njZEBAoIBAQCuI/dv6opNniMmOY+iI1Oh6kyNnctKe/VF2SqwfeK3I6fCfcNEm46u\nQHnJWqVCIQ1ZsU8b/Pn0+mJYB/n+OYh585DsDR2YEJPXE+qrVHG1qIEEyTgcTb30\n+nehJ6d5SYMej4VMdepgMD7HeG7Nly1wwu0VYTUTLP3Z812NUdnBYmb5QSRnsHrk\nOVlAkJAaunAk1O3rwwdyGwMmNVIgzf0foa/c8uFQCbUHocQUwr//vN+U+Nhi4VQC\nnRiPT36rWxLqwyn8sa7dcE9A6Hp2KDBc97DJTDNvA3StF1JHAf/EoiTq3mWvVNhR\nmPN+iWirFH2RN5KkSaTzEtrXAgbLvic9\n-----END PRIVATE KEY-----\n",
|
||||
"gatekeeper_cns": "disk0book.fritz.box",
|
||||
"proxy_datapath": "/Users/jash/.cyphernode/proxy",
|
||||
"lightning_implementation": "c-lightning",
|
||||
"lightning_datapath": "/Users/jash/.cyphernode/lightning",
|
||||
"lightning_nodename": "🚀 Disciplined Dormouse 🚀",
|
||||
"lightning_nodecolor": "ff0000",
|
||||
"otsclient_datapath": "/Users/jash/.cyphernode/otsclient",
|
||||
"adminhash": "BsJFlh7q4JmwI\n",
|
||||
"use_xpub": true,
|
||||
"xpub": "abc",
|
||||
"derivation_path": "da",
|
||||
"gatekeeper_clientkeyspassword": "test123",
|
||||
"gatekeeper_clientkeyspassword_c": "test123",
|
||||
"gatekeeper_statuspw": "dasfdssad",
|
||||
"gatekeeper_datapath": "/Users/jash/.cyphernode/gatekeeper",
|
||||
"traefik_datapath": "foo",
|
||||
"lightning_external_ip": "123.123.123.123"
|
||||
|
||||
}
|
||||
52
cyphernodeconf_docker/test/data/config.0.2.0.json
Normal file
52
cyphernodeconf_docker/test/data/config.0.2.0.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"schema_version": "0.2.0",
|
||||
"features": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"net": "testnet",
|
||||
"use_xpub": true,
|
||||
"installer_mode": "docker",
|
||||
"run_as_different_user": true,
|
||||
"username": "cyphernode",
|
||||
"docker_mode": "compose",
|
||||
"bitcoin_rpcuser": "bitcoin",
|
||||
"bitcoin_rpcpassword": "test123",
|
||||
"bitcoin_uacomment": "",
|
||||
"bitcoin_prune": false,
|
||||
"bitcoin_prune_size": 550,
|
||||
"bitcoin_datapath": "/Users/jash/.cyphernode/bitcoin",
|
||||
"bitcoin_mode": "internal",
|
||||
"bitcoin_expose": false,
|
||||
"lightning_expose": true,
|
||||
"gatekeeper_keys": {
|
||||
"configEntries": [
|
||||
"kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
],
|
||||
"clientInformation": [
|
||||
"000=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
},
|
||||
"gatekeeper_sslcert": "-----BEGIN CERTIFICATE-----\nMIIE/jCCAuagAwIBAgIJAIBv4aiI2NRtMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV\nBAMME2Rpc2swYm9vay5mcml0ei5ib3gwHhcNMTkwMTE3MTcwMDA5WhcNMjkwMTE0\nMTcwMDA5WjAeMRwwGgYDVQQDDBNkaXNrMGJvb2suZnJpdHouYm94MIICIjANBgkq\nhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyVKNTGlPfd4QX9HaDc9a6prbc9il4jtR\niChSlMf3/6UfAMcS+xVgR2iR8FK/DQuFzxn+6BybpoiD333rjDr7zR7y9px3Xph2\nbmsjZy0hv9SIBbx0DJvvwODTlWTAH8qgU2DN6xWc7vjgeGi5uTpnmwWrkH6BjtVr\nwoBkF0JmfH7KiLS/QjWqPKeI6o/GpvCP9meD131Sq/ReoOTrJ4F5aNdhAril4nU5\n6e7Y+Iyp35DZSLuU+pDJAhxEvkYGas1ted5RRxlho8ukaoABCbmaTeNmgsJxK2SC\nABjfUc38aAlNLuMbMMR7Q85Z84OTJiUqanVczwdSj1QHlNCWZK1McBPhj2m2Wdge\ngedrq5XcjQGChzTEozcFntU0qzY3ja1+DOE8UaMaTrDH4saUXCMZk3W1m5mmiZW3\nmcB0cKGdeg6K6USg1BwBTU9qolUusxz5T0tNxjcMlXU93P17d4s5IXfliXhMNr/6\n4fl78Ey3FNprTix4alW7hBAp/eA/LhS55s3jwdoVzJl4RELC0284pahj5exYQwU6\nzjLedMxzC+7veQYwWfZOs9jVCTP0YStuT0j9xD3ausLZyB1Egbsajyy71IeoYOf1\n9S6dFIXE5LHAw2j3D3bh5wb019I8V5szGbeemdBpb3m+bzT8qjLSNranuT41CIHd\nIYjq85vDEJsCAwEAAaM/MD0wOwYDVR0RBDQwMoITZGlzazBib29rLmZyaXR6LmJv\neIIJbG9jYWxob3N0ggpnYXRla2VlcGVyhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC\nAQBrE4bJsIMwSRPng94PcqR5F6Cux0bkwezALJCHpjHTuqok/wHHE5dZsAXcSsYc\n5givuBESih6CpY5h21Od0TBugyv3FCRY8OoaBXtlO6FYlEnVeJ8AOexJTb3qcbBS\nHU8MBWEydUh5HFA3PRKAG0Y4cvUK4WXJZ42Et3td0NkGFOv6bxdtVGB4Vz7FGn+3\nqd9fpmFCdQYDp6RSZDDz4B8XLsVuTeTES5GbUMSQAGanP7jxMr04wQ3MuoZrRODN\nFatifOJfq0fZddsBjJbrTLxArIqaPh3J4xzwiNE5du4CQDQrbbHXG22kuvbr5foA\ncixLnuyWMq0a5a70mSNS6TZ3nq4ATXNNa0cZ8fBxHqHGTLM8gQisW8vTaZfIFh/i\nhnFcGxtpo1ryi7JG9HCWsh0x20677iag5MuZfv2s4TbK71Ol6WV4FravCqU0qgbn\nTTl+BnYw3H67FO/a6RD4ISlFWK+8EVEQdMgvPoRuw323YznT0Nd8Q/Gq8raYF2wa\nz9T9OXu6TcVGtfPAgX+AM/+hDqWGxyiFR9ZtLpGOHGP8f+TZA5uCawc8Zry4yN6L\nE0yPIx96pJz59T3k8XbRHTQCaPsSUGRAZIY9LpJj0fIG7zCr9eCBpp2qyzmpyNfx\negN3ILYy1Y8JbJj73HWyP0F3Am7i76tkCWB7tQeFOb5FMg==\n-----END CERTIFICATE-----\n",
|
||||
"gatekeeper_sslkey": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJUo1MaU993hBf\n0doNz1rqmttz2KXiO1GIKFKUx/f/pR8AxxL7FWBHaJHwUr8NC4XPGf7oHJumiIPf\nfeuMOvvNHvL2nHdemHZuayNnLSG/1IgFvHQMm+/A4NOVZMAfyqBTYM3rFZzu+OB4\naLm5OmebBauQfoGO1WvCgGQXQmZ8fsqItL9CNao8p4jqj8am8I/2Z4PXfVKr9F6g\n5OsngXlo12ECuKXidTnp7tj4jKnfkNlIu5T6kMkCHES+RgZqzW153lFHGWGjy6Rq\ngAEJuZpN42aCwnErZIIAGN9RzfxoCU0u4xswxHtDzlnzg5MmJSpqdVzPB1KPVAeU\n0JZkrUxwE+GPabZZ2B6B52urldyNAYKHNMSjNwWe1TSrNjeNrX4M4TxRoxpOsMfi\nxpRcIxmTdbWbmaaJlbeZwHRwoZ16DorpRKDUHAFNT2qiVS6zHPlPS03GNwyVdT3c\n/Xt3izkhd+WJeEw2v/rh+XvwTLcU2mtOLHhqVbuEECn94D8uFLnmzePB2hXMmXhE\nQsLTbzilqGPl7FhDBTrOMt50zHML7u95BjBZ9k6z2NUJM/RhK25PSP3EPdq6wtnI\nHUSBuxqPLLvUh6hg5/X1Lp0UhcTkscDDaPcPduHnBvTX0jxXmzMZt56Z0Glveb5v\nNPyqMtI2tqe5PjUIgd0hiOrzm8MQmwIDAQABAoICAQCI5uA7M+ngd9++qR+VAIqc\nus28y3iSjS/2XSU7E3irmYepqbZYk8KzDIMhX8OXhVxq5wyWns2hw3eZxTEmXP3a\nEM+7r87kvtzaXXTntqMapdYRwINSB8BT8w8uqiKT++Bmko+06y+auhc7Ckwxj2vg\n2Uw/qCdGEA+FZnWp83dp9XaY3ACrb37iXDMY/shhwXjEYMQhB5HuaPDojIL0jHEZ\nQE0x4oq7omfNkqRs8IqcAw4fDaBTe52VF9APa+L1QdjOZMX0iWgCUHrwCTere1FY\n4ehVxw/aKDDXDBLguCiKPrkDx2A4G4SPKYW1uKWZ7PAZENIZ3qrf2I6HPgjnUYmG\nAHQiR3JcwsXFZZAMW/kbqzRCS7CrvNnrzcUL9JAlpFmMDeAFIlbVkFED+kOtVioR\nPAcDWKtlWOWbX3Kn218FCblH86XdzB9H/pgbHxf8cXFcnaqVApxC1zv+uIaV526p\nU9maF8CMVX5bZ21e+dpP1BQ3DDRn3DCQno/QrGGMxK66EBVrHrllHBYpUppcj4w8\nCn5RDhp2KJjYsgX9zjuXif1gdP2jqBCDWCog+YwsoQP1Qp613D6rl5TKaa/rfZmX\nfG5Q98/wfAHwLjIDfwCXWqKOFoGdMzxg9hxk0bSNn64m1UY6OYB4yJs/o6spqGXq\nZRaX2LStSq5fhvB+tjl3AQKCAQEA7wBS3t2dHOVLZGVycxSq1LRoeW44KxZaNSiv\nXj5Xrw/jAnnAEAcVFrCGFY40MC4SNM//VUWHs4zKXxAhK/vFOgSvmnNh6mWDd/sO\nFyzo17fhwhi4u8BIjvchHgwg5aMi4uCLFM8RBZjI5MpMyNBwxT5VLKKKv3N9YtUK\n5JPhXkZJnhOhKj3vTaCeJFBMvcknoqqNcUcEEMo0d+YqUAYh9+8qpjBSKBV+8/VZ\nbQwyjd+EN5ajqRLIjKOy1GrbRwBcxadGdZzqMDYlSVBdAxAssPiUqhTLeHAuGfbK\nB0iF0DLgwl0N/6qx0WtLTA3MY7NcQJ/cjUioJKqbnvouEP2f2wKCAQEA16Qulg5I\nyDbJsiFzmeLNpKSXeh9y1q/hvqizd8R9GAUh9TVSgAnoiCpucguvMYBsXeGU3srr\no9AvxmkmrMWVNZbolaMRv0p+nXPR6uF5tFQJ/jYm1H/jI3ieF2ZXJveQBkqjRsOD\nsWI4HNuarGnsOo8rqV0ybYGFks1dhuvBZp7RemZbXqaFnk/D8FtkmHRsWUdEQifd\njHCATRbUHjAm8tk7HjdJbhYIMCZkbN1HRkx044pk+os37Eqi4Ok+s/MbQ7g2lY+R\nt1sxrJo4dsqxwflz9U6U5ECwH2hxuzpowGXwfcqtPLvVJT1p+TUxIHNb2eUvJg7R\ntsx4BzC4FbaOQQKCAQBMaHYV+hO4oTWxKx2j3P+gcOzVpX8fh03foNov7w5pUVLp\n/7J/bUQ4tMapLYVRwejgKX8f69KufFxWd/mi7iLnoYfigPDU1w9o1EJ09k6aaJcs\nTmsA20BXNHrJ+GasA7OrhM3yISD4ARh4zJQvvzPOW1cvpedlva5gYmvRF9X9Jctz\nViM4NgTDdI4aXfGq2xxozM3bYTDTjVGo41SzsMI7WaLw7pHVbsnfhJjKXBPtd3/1\npZs8+lxTWiJZ9q+Ty3HdXtUP1NKqByV0gtS5nforuc0Ncwh5wKN8eYGtQmpFXX6t\npMJM8m1W3k58Sg0F8tmTb4g7Qvc+gayu7py7odnxAoIBAQC5pJqGjF2UH7acJ7hB\nrsOjDh9p/1D6Cgip/soiPYID/8cwNmuD1wPc1cqnW+/DCfBBEkb7Vm5uZHf8s+Gb\ns620qOoqiGxq5lMCcgcx0lLYL9E6jJv5LO/6RYi0VGKLJW1UPUU7Um45c3kjPtt1\nuuqnr0HDWHxlUbAQpcPyt1uUYP2uBhh675jwpXLlpYiAxxnP8k6NNYzBrsiFlTWf\nl4ywXNtMNAR/RXBfI24pWoZVutSWXzp3hwrp3YdDYQmeGZhLQHedYi/sThIBqfMa\nMX2+pGZztObxac81+tCOgsZTfG8BnE5vjrT8jlaBOI3Ghgl5GJjyhqd8W6KpbgPM\njZEBAoIBAQCuI/dv6opNniMmOY+iI1Oh6kyNnctKe/VF2SqwfeK3I6fCfcNEm46u\nQHnJWqVCIQ1ZsU8b/Pn0+mJYB/n+OYh585DsDR2YEJPXE+qrVHG1qIEEyTgcTb30\n+nehJ6d5SYMej4VMdepgMD7HeG7Nly1wwu0VYTUTLP3Z812NUdnBYmb5QSRnsHrk\nOVlAkJAaunAk1O3rwwdyGwMmNVIgzf0foa/c8uFQCbUHocQUwr//vN+U+Nhi4VQC\nnRiPT36rWxLqwyn8sa7dcE9A6Hp2KDBc97DJTDNvA3StF1JHAf/EoiTq3mWvVNhR\nmPN+iWirFH2RN5KkSaTzEtrXAgbLvic9\n-----END PRIVATE KEY-----\n",
|
||||
"gatekeeper_cns": "disk0book.fritz.box",
|
||||
"proxy_datapath": "/Users/jash/.cyphernode/proxy",
|
||||
"lightning_implementation": "c-lightning",
|
||||
"lightning_datapath": "/Users/jash/.cyphernode/lightning",
|
||||
"lightning_nodename": "🚀 Disciplined Dormouse 🚀",
|
||||
"lightning_nodecolor": "ff0000",
|
||||
"otsclient_datapath": "/Users/jash/.cyphernode/otsclient",
|
||||
"adminhash": "BsJFlh7q4JmwI\n",
|
||||
"use_xpub": true,
|
||||
"xpub": "abc",
|
||||
"derivation_path": "da",
|
||||
"gatekeeper_clientkeyspassword": "test123",
|
||||
"gatekeeper_datapath": "/Users/jash/.cyphernode/gatekeeper",
|
||||
"traefik_datapath": "foo"
|
||||
}
|
||||
59
cyphernodeconf_docker/test/data/config.0.2.2.json
Normal file
59
cyphernodeconf_docker/test/data/config.0.2.2.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"schema_version": "0.2.2",
|
||||
"setup_version": "setup_version",
|
||||
"docker_versions": {},
|
||||
"features": [
|
||||
"lightning",
|
||||
"otsclient"
|
||||
],
|
||||
"net": "testnet",
|
||||
"use_xpub": true,
|
||||
"installer_mode": "docker",
|
||||
"run_as_different_user": true,
|
||||
"username": "cyphernode",
|
||||
"docker_mode": "compose",
|
||||
"bitcoin_rpcuser": "bitcoin",
|
||||
"bitcoin_rpcpassword": "test123",
|
||||
"bitcoin_uacomment": "",
|
||||
"bitcoin_prune": false,
|
||||
"bitcoin_prune_size": 550,
|
||||
"bitcoin_datapath": "/Users/jash/.cyphernode/bitcoin",
|
||||
"bitcoin_mode": "internal",
|
||||
"bitcoin_expose": false,
|
||||
"lightning_expose": true,
|
||||
"gatekeeper_port": 2009,
|
||||
"gatekeeper_keys": {
|
||||
"configEntries": [
|
||||
"kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
|
||||
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
|
||||
],
|
||||
"clientInformation": [
|
||||
"000=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
|
||||
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
|
||||
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
|
||||
]
|
||||
},
|
||||
"gatekeeper_sslcert": "-----BEGIN CERTIFICATE-----\nMIIE/jCCAuagAwIBAgIJAIBv4aiI2NRtMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV\nBAMME2Rpc2swYm9vay5mcml0ei5ib3gwHhcNMTkwMTE3MTcwMDA5WhcNMjkwMTE0\nMTcwMDA5WjAeMRwwGgYDVQQDDBNkaXNrMGJvb2suZnJpdHouYm94MIICIjANBgkq\nhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyVKNTGlPfd4QX9HaDc9a6prbc9il4jtR\niChSlMf3/6UfAMcS+xVgR2iR8FK/DQuFzxn+6BybpoiD333rjDr7zR7y9px3Xph2\nbmsjZy0hv9SIBbx0DJvvwODTlWTAH8qgU2DN6xWc7vjgeGi5uTpnmwWrkH6BjtVr\nwoBkF0JmfH7KiLS/QjWqPKeI6o/GpvCP9meD131Sq/ReoOTrJ4F5aNdhAril4nU5\n6e7Y+Iyp35DZSLuU+pDJAhxEvkYGas1ted5RRxlho8ukaoABCbmaTeNmgsJxK2SC\nABjfUc38aAlNLuMbMMR7Q85Z84OTJiUqanVczwdSj1QHlNCWZK1McBPhj2m2Wdge\ngedrq5XcjQGChzTEozcFntU0qzY3ja1+DOE8UaMaTrDH4saUXCMZk3W1m5mmiZW3\nmcB0cKGdeg6K6USg1BwBTU9qolUusxz5T0tNxjcMlXU93P17d4s5IXfliXhMNr/6\n4fl78Ey3FNprTix4alW7hBAp/eA/LhS55s3jwdoVzJl4RELC0284pahj5exYQwU6\nzjLedMxzC+7veQYwWfZOs9jVCTP0YStuT0j9xD3ausLZyB1Egbsajyy71IeoYOf1\n9S6dFIXE5LHAw2j3D3bh5wb019I8V5szGbeemdBpb3m+bzT8qjLSNranuT41CIHd\nIYjq85vDEJsCAwEAAaM/MD0wOwYDVR0RBDQwMoITZGlzazBib29rLmZyaXR6LmJv\neIIJbG9jYWxob3N0ggpnYXRla2VlcGVyhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC\nAQBrE4bJsIMwSRPng94PcqR5F6Cux0bkwezALJCHpjHTuqok/wHHE5dZsAXcSsYc\n5givuBESih6CpY5h21Od0TBugyv3FCRY8OoaBXtlO6FYlEnVeJ8AOexJTb3qcbBS\nHU8MBWEydUh5HFA3PRKAG0Y4cvUK4WXJZ42Et3td0NkGFOv6bxdtVGB4Vz7FGn+3\nqd9fpmFCdQYDp6RSZDDz4B8XLsVuTeTES5GbUMSQAGanP7jxMr04wQ3MuoZrRODN\nFatifOJfq0fZddsBjJbrTLxArIqaPh3J4xzwiNE5du4CQDQrbbHXG22kuvbr5foA\ncixLnuyWMq0a5a70mSNS6TZ3nq4ATXNNa0cZ8fBxHqHGTLM8gQisW8vTaZfIFh/i\nhnFcGxtpo1ryi7JG9HCWsh0x20677iag5MuZfv2s4TbK71Ol6WV4FravCqU0qgbn\nTTl+BnYw3H67FO/a6RD4ISlFWK+8EVEQdMgvPoRuw323YznT0Nd8Q/Gq8raYF2wa\nz9T9OXu6TcVGtfPAgX+AM/+hDqWGxyiFR9ZtLpGOHGP8f+TZA5uCawc8Zry4yN6L\nE0yPIx96pJz59T3k8XbRHTQCaPsSUGRAZIY9LpJj0fIG7zCr9eCBpp2qyzmpyNfx\negN3ILYy1Y8JbJj73HWyP0F3Am7i76tkCWB7tQeFOb5FMg==\n-----END CERTIFICATE-----\n",
|
||||
"gatekeeper_sslkey": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJUo1MaU993hBf\n0doNz1rqmttz2KXiO1GIKFKUx/f/pR8AxxL7FWBHaJHwUr8NC4XPGf7oHJumiIPf\nfeuMOvvNHvL2nHdemHZuayNnLSG/1IgFvHQMm+/A4NOVZMAfyqBTYM3rFZzu+OB4\naLm5OmebBauQfoGO1WvCgGQXQmZ8fsqItL9CNao8p4jqj8am8I/2Z4PXfVKr9F6g\n5OsngXlo12ECuKXidTnp7tj4jKnfkNlIu5T6kMkCHES+RgZqzW153lFHGWGjy6Rq\ngAEJuZpN42aCwnErZIIAGN9RzfxoCU0u4xswxHtDzlnzg5MmJSpqdVzPB1KPVAeU\n0JZkrUxwE+GPabZZ2B6B52urldyNAYKHNMSjNwWe1TSrNjeNrX4M4TxRoxpOsMfi\nxpRcIxmTdbWbmaaJlbeZwHRwoZ16DorpRKDUHAFNT2qiVS6zHPlPS03GNwyVdT3c\n/Xt3izkhd+WJeEw2v/rh+XvwTLcU2mtOLHhqVbuEECn94D8uFLnmzePB2hXMmXhE\nQsLTbzilqGPl7FhDBTrOMt50zHML7u95BjBZ9k6z2NUJM/RhK25PSP3EPdq6wtnI\nHUSBuxqPLLvUh6hg5/X1Lp0UhcTkscDDaPcPduHnBvTX0jxXmzMZt56Z0Glveb5v\nNPyqMtI2tqe5PjUIgd0hiOrzm8MQmwIDAQABAoICAQCI5uA7M+ngd9++qR+VAIqc\nus28y3iSjS/2XSU7E3irmYepqbZYk8KzDIMhX8OXhVxq5wyWns2hw3eZxTEmXP3a\nEM+7r87kvtzaXXTntqMapdYRwINSB8BT8w8uqiKT++Bmko+06y+auhc7Ckwxj2vg\n2Uw/qCdGEA+FZnWp83dp9XaY3ACrb37iXDMY/shhwXjEYMQhB5HuaPDojIL0jHEZ\nQE0x4oq7omfNkqRs8IqcAw4fDaBTe52VF9APa+L1QdjOZMX0iWgCUHrwCTere1FY\n4ehVxw/aKDDXDBLguCiKPrkDx2A4G4SPKYW1uKWZ7PAZENIZ3qrf2I6HPgjnUYmG\nAHQiR3JcwsXFZZAMW/kbqzRCS7CrvNnrzcUL9JAlpFmMDeAFIlbVkFED+kOtVioR\nPAcDWKtlWOWbX3Kn218FCblH86XdzB9H/pgbHxf8cXFcnaqVApxC1zv+uIaV526p\nU9maF8CMVX5bZ21e+dpP1BQ3DDRn3DCQno/QrGGMxK66EBVrHrllHBYpUppcj4w8\nCn5RDhp2KJjYsgX9zjuXif1gdP2jqBCDWCog+YwsoQP1Qp613D6rl5TKaa/rfZmX\nfG5Q98/wfAHwLjIDfwCXWqKOFoGdMzxg9hxk0bSNn64m1UY6OYB4yJs/o6spqGXq\nZRaX2LStSq5fhvB+tjl3AQKCAQEA7wBS3t2dHOVLZGVycxSq1LRoeW44KxZaNSiv\nXj5Xrw/jAnnAEAcVFrCGFY40MC4SNM//VUWHs4zKXxAhK/vFOgSvmnNh6mWDd/sO\nFyzo17fhwhi4u8BIjvchHgwg5aMi4uCLFM8RBZjI5MpMyNBwxT5VLKKKv3N9YtUK\n5JPhXkZJnhOhKj3vTaCeJFBMvcknoqqNcUcEEMo0d+YqUAYh9+8qpjBSKBV+8/VZ\nbQwyjd+EN5ajqRLIjKOy1GrbRwBcxadGdZzqMDYlSVBdAxAssPiUqhTLeHAuGfbK\nB0iF0DLgwl0N/6qx0WtLTA3MY7NcQJ/cjUioJKqbnvouEP2f2wKCAQEA16Qulg5I\nyDbJsiFzmeLNpKSXeh9y1q/hvqizd8R9GAUh9TVSgAnoiCpucguvMYBsXeGU3srr\no9AvxmkmrMWVNZbolaMRv0p+nXPR6uF5tFQJ/jYm1H/jI3ieF2ZXJveQBkqjRsOD\nsWI4HNuarGnsOo8rqV0ybYGFks1dhuvBZp7RemZbXqaFnk/D8FtkmHRsWUdEQifd\njHCATRbUHjAm8tk7HjdJbhYIMCZkbN1HRkx044pk+os37Eqi4Ok+s/MbQ7g2lY+R\nt1sxrJo4dsqxwflz9U6U5ECwH2hxuzpowGXwfcqtPLvVJT1p+TUxIHNb2eUvJg7R\ntsx4BzC4FbaOQQKCAQBMaHYV+hO4oTWxKx2j3P+gcOzVpX8fh03foNov7w5pUVLp\n/7J/bUQ4tMapLYVRwejgKX8f69KufFxWd/mi7iLnoYfigPDU1w9o1EJ09k6aaJcs\nTmsA20BXNHrJ+GasA7OrhM3yISD4ARh4zJQvvzPOW1cvpedlva5gYmvRF9X9Jctz\nViM4NgTDdI4aXfGq2xxozM3bYTDTjVGo41SzsMI7WaLw7pHVbsnfhJjKXBPtd3/1\npZs8+lxTWiJZ9q+Ty3HdXtUP1NKqByV0gtS5nforuc0Ncwh5wKN8eYGtQmpFXX6t\npMJM8m1W3k58Sg0F8tmTb4g7Qvc+gayu7py7odnxAoIBAQC5pJqGjF2UH7acJ7hB\nrsOjDh9p/1D6Cgip/soiPYID/8cwNmuD1wPc1cqnW+/DCfBBEkb7Vm5uZHf8s+Gb\ns620qOoqiGxq5lMCcgcx0lLYL9E6jJv5LO/6RYi0VGKLJW1UPUU7Um45c3kjPtt1\nuuqnr0HDWHxlUbAQpcPyt1uUYP2uBhh675jwpXLlpYiAxxnP8k6NNYzBrsiFlTWf\nl4ywXNtMNAR/RXBfI24pWoZVutSWXzp3hwrp3YdDYQmeGZhLQHedYi/sThIBqfMa\nMX2+pGZztObxac81+tCOgsZTfG8BnE5vjrT8jlaBOI3Ghgl5GJjyhqd8W6KpbgPM\njZEBAoIBAQCuI/dv6opNniMmOY+iI1Oh6kyNnctKe/VF2SqwfeK3I6fCfcNEm46u\nQHnJWqVCIQ1ZsU8b/Pn0+mJYB/n+OYh585DsDR2YEJPXE+qrVHG1qIEEyTgcTb30\n+nehJ6d5SYMej4VMdepgMD7HeG7Nly1wwu0VYTUTLP3Z812NUdnBYmb5QSRnsHrk\nOVlAkJAaunAk1O3rwwdyGwMmNVIgzf0foa/c8uFQCbUHocQUwr//vN+U+Nhi4VQC\nnRiPT36rWxLqwyn8sa7dcE9A6Hp2KDBc97DJTDNvA3StF1JHAf/EoiTq3mWvVNhR\nmPN+iWirFH2RN5KkSaTzEtrXAgbLvic9\n-----END PRIVATE KEY-----\n",
|
||||
"gatekeeper_cns": "disk0book.fritz.box",
|
||||
"proxy_datapath": "/Users/jash/.cyphernode/proxy",
|
||||
"lightning_implementation": "c-lightning",
|
||||
"lightning_datapath": "/Users/jash/.cyphernode/lightning",
|
||||
"lightning_nodename": "🚀 Disciplined Dormouse 🚀",
|
||||
"lightning_nodecolor": "ff0000",
|
||||
"otsclient_datapath": "/Users/jash/.cyphernode/otsclient",
|
||||
"adminhash": "BsJFlh7q4JmwI\n",
|
||||
"use_xpub": true,
|
||||
"xpub": "abc",
|
||||
"derivation_path": "da",
|
||||
"gatekeeper_clientkeyspassword": "test123",
|
||||
"gatekeeper_datapath": "/Users/jash/.cyphernode/gatekeeper",
|
||||
"traefik_datapath": "foo",
|
||||
"lightning_announce": true,
|
||||
"gatekeeper_expose": false,
|
||||
"traefik_http_port": 80,
|
||||
"traefik_https_port": 443
|
||||
}
|
||||
6
cyphernodeconf_docker/test/htpasswd.test.js
Normal file
6
cyphernodeconf_docker/test/htpasswd.test.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const htpasswd = require('../lib/htpasswd.js');
|
||||
|
||||
test( 'generate htpasswd', async ()=>{
|
||||
const pw = await htpasswd( 'test123' );
|
||||
expect( pw ).not.toBe( undefined );
|
||||
});
|
||||
7
cyphernodeconf_docker/test/name.test.js
Normal file
7
cyphernodeconf_docker/test/name.test.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const name = require('../lib/name.js');
|
||||
|
||||
|
||||
test( 'Create new random name', ()=>{
|
||||
const n = name.generate();
|
||||
expect( n ).not.toBe( undefined )
|
||||
});
|
||||
26
dist/config.json.sample
vendored
26
dist/config.json.sample
vendored
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"derivation_path": "0/n",
|
||||
"installer": "docker",
|
||||
"features": [
|
||||
"lightning",
|
||||
"otsclient",
|
||||
"electrum"
|
||||
],
|
||||
"net": "testnet",
|
||||
"xpub": "upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb",
|
||||
"bitcoin_mode": "internal",
|
||||
"bitcoin_rpcuser": "user",
|
||||
"bitcoin_rpcpassword": "password",
|
||||
"bitcoin_prune": false,
|
||||
"bitcoin_uacomment": "",
|
||||
"lightning_implementation": "c-lightning",
|
||||
"lightning_nodename": "SatoshiPortal",
|
||||
"lightning_nodecolor": "ff00ff",
|
||||
"electrum_implementation": "eps",
|
||||
"installer_mode": "docker",
|
||||
"proxy_datapath": "/tmp/p",
|
||||
"bitcoin_datapath": "/tmp/b",
|
||||
"lightning_datapath": "/tmp/l",
|
||||
"bitcoin_expose": false,
|
||||
"devmode": true
|
||||
}
|
||||
241
dist/setup.sh
vendored
241
dist/setup.sh
vendored
@@ -110,7 +110,7 @@ sudo_if_required() {
|
||||
}
|
||||
|
||||
modify_permissions() {
|
||||
local directories=("installer" "gatekeeper" "lightning" "bitcoin" "docker-compose.yaml" "$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH")
|
||||
local directories=("installer" "gatekeeper" "lightning" "bitcoin" "docker-compose.yaml" "traefik" "$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$TRAEFIK_DATAPATH")
|
||||
for d in "${directories[@]}"
|
||||
do
|
||||
if [[ -e $d ]]; then
|
||||
@@ -122,7 +122,7 @@ modify_permissions() {
|
||||
}
|
||||
|
||||
modify_owner() {
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH")
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$TRAEFIK_DATAPATH")
|
||||
local user=$(id -u $RUN_AS_USER):$(id -g $RUN_AS_USER)
|
||||
for d in "${directories[@]}"
|
||||
do
|
||||
@@ -139,7 +139,7 @@ configure() {
|
||||
local recreate=""
|
||||
|
||||
if [[ $1 == 1 ]]; then
|
||||
recreate="recreate"
|
||||
recreate=" recreate"
|
||||
fi
|
||||
|
||||
|
||||
@@ -151,8 +151,6 @@ configure() {
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
interactive=' -it'
|
||||
else
|
||||
gen_options=' --force 2'
|
||||
fi
|
||||
|
||||
if [[ $CFG_PASSWORD ]]; then
|
||||
@@ -183,21 +181,24 @@ configure() {
|
||||
# configure features of cyphernode
|
||||
docker run -v $current_path:/data \
|
||||
-e DEFAULT_USER=$USER \
|
||||
-e DEFAULT_DATADIR_BASE=$HOME \
|
||||
-e SETUP_DIR=$SETUP_DIR \
|
||||
-e DEFAULT_CERT_HOSTNAME=$(hostname) \
|
||||
-e VERSION_OVERRIDE=$VERSION_OVERRIDE \
|
||||
-e GATEKEEPER_VERSION=$GATEKEEPER_VERSION \
|
||||
-e PROXY_VERSION=$PROXY_VERSION \
|
||||
-e NOTIFIER_VERSION=$NOTIFIER_VERSION \
|
||||
-e PROXYCRON_VERSION=$PROXYCRON_VERSION \
|
||||
-e OTSCLIENT_VERSION=$OTSCLIENT_VERSION \
|
||||
-e PYCOIN_VERSION=$PYCOIN_VERSION \
|
||||
-e BITCOIN_VERSION=$BITCOIN_VERSION \
|
||||
-e LIGHTNING_VERSION=$LIGHTNING_VERSION \
|
||||
-e SETUP_VERSION=$SETUP_VERSION \
|
||||
--log-driver=none$pw_env \
|
||||
--network none \
|
||||
--rm$interactive cyphernode/cyphernodeconf:$CONF_VERSION $user yo --no-insight cyphernode$gen_options $recreate
|
||||
if [[ -f $current_path/exitStatus.sh ]]; then
|
||||
. $current_path/exitStatus.sh
|
||||
rm $current_path/exitStatus.sh
|
||||
--rm$interactive cyphernode/cyphernodeconf:$CONF_VERSION $user node index.js$recreate
|
||||
if [[ -f $cyphernodeconf_filepath/exitStatus.sh ]]; then
|
||||
. $cyphernodeconf_filepath/exitStatus.sh
|
||||
rm $cyphernodeconf_filepath/exitStatus.sh
|
||||
fi
|
||||
|
||||
if [[ ! $EXIT_STATUS == 0 ]]; then
|
||||
@@ -348,7 +349,6 @@ compare_bitcoinconf() {
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
|
||||
local archpath=$(uname -m)
|
||||
|
||||
# compat mode for SatoshiPortal repo
|
||||
@@ -363,36 +363,48 @@ install_docker() {
|
||||
next
|
||||
fi
|
||||
|
||||
if [ -d $GATEKEEPER_DATAPATH ]; then
|
||||
if [[ ! -f $GATEKEEPER_DATAPATH/installation.json ]]; then
|
||||
# prevent mounting installation.json as a directory
|
||||
sudo_if_required touch $GATEKEEPER_DATAPATH/installation.json
|
||||
fi
|
||||
|
||||
if [[ ! -d $GATEKEEPER_DATAPATH/certs ]]; then
|
||||
sudo_if_required mkdir -p $GATEKEEPER_DATAPATH/certs > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [[ ! -d $GATEKEEPER_DATAPATH/private ]]; then
|
||||
sudo_if_required mkdir -p $GATEKEEPER_DATAPATH/private > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
copy_file $current_path/gatekeeper/api.properties $GATEKEEPER_DATAPATH/api.properties 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/gatekeeper/keys.properties $GATEKEEPER_DATAPATH/keys.properties 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/config.7z $GATEKEEPER_DATAPATH/config.7z 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/client.7z $GATEKEEPER_DATAPATH/client.7z 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/gatekeeper/cert.pem $GATEKEEPER_DATAPATH/certs/cert.pem 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/gatekeeper/key.pem $GATEKEEPER_DATAPATH/private/key.pem 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/gatekeeper/htpasswd $GATEKEEPER_DATAPATH/htpasswd 1 $SUDO_REQUIRED
|
||||
if [[ ! -f $GATEKEEPER_DATAPATH/installation.json ]]; then
|
||||
# prevent mounting installation.json as a directory
|
||||
sudo_if_required touch $GATEKEEPER_DATAPATH/installation.json
|
||||
fi
|
||||
|
||||
if [[ ! -d $GATEKEEPER_DATAPATH/certs ]]; then
|
||||
sudo_if_required mkdir -p $GATEKEEPER_DATAPATH/certs > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [[ ! -d $GATEKEEPER_DATAPATH/private ]]; then
|
||||
sudo_if_required mkdir -p $GATEKEEPER_DATAPATH/private > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
copy_file $cyphernodeconf_filepath/gatekeeper/default.conf $GATEKEEPER_DATAPATH/default.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/gatekeeper/api.properties $GATEKEEPER_DATAPATH/api.properties 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/gatekeeper/keys.properties $GATEKEEPER_DATAPATH/keys.properties 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/config.7z $GATEKEEPER_DATAPATH/config.7z 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/client.7z $GATEKEEPER_DATAPATH/client.7z 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/gatekeeper/cert.pem $GATEKEEPER_DATAPATH/certs/cert.pem 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/gatekeeper/key.pem $GATEKEEPER_DATAPATH/private/key.pem 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/traefik/htpasswd $GATEKEEPER_DATAPATH/htpasswd 1 $SUDO_REQUIRED
|
||||
|
||||
|
||||
if [ ! -d $TRAEFIK_DATAPATH ]; then
|
||||
step " [32mcreate[0m $TRAEFIK_DATAPATH"
|
||||
sudo_if_required mkdir -p $TRAEFIK_DATAPATH
|
||||
next
|
||||
fi
|
||||
|
||||
copy_file $cyphernodeconf_filepath/traefik/acme.json $TRAEFIK_DATAPATH/acme.json 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/traefik/traefik.toml $TRAEFIK_DATAPATH/traefik.toml 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/traefik/htpasswd $TRAEFIK_DATAPATH/htpasswd 1 $SUDO_REQUIRED
|
||||
|
||||
|
||||
if [ ! -d $PROXY_DATAPATH ]; then
|
||||
step " [32mcreate[0m $PROXY_DATAPATH"
|
||||
sudo_if_required mkdir -p $PROXY_DATAPATH
|
||||
next
|
||||
fi
|
||||
|
||||
copy_file $current_path/installer/config.sh $PROXY_DATAPATH/config.sh 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/installer/config.sh $PROXY_DATAPATH/config.sh 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/cyphernode/info.json $PROXY_DATAPATH/info.json 1 $SUDO_REQUIRED
|
||||
|
||||
if [[ $BITCOIN_INTERNAL == true ]]; then
|
||||
if [ ! -d $BITCOIN_DATAPATH ]; then
|
||||
@@ -402,18 +414,22 @@ install_docker() {
|
||||
fi
|
||||
if [ -d $BITCOIN_DATAPATH ]; then
|
||||
|
||||
local cmpStatus=$(compare_bitcoinconf $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf)
|
||||
local cmpStatus=$(compare_bitcoinconf $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf)
|
||||
|
||||
if [[ $cmpStatus == 'dataloss' ]]; then
|
||||
if [[ $ALWAYSYES == 1 ]]; then
|
||||
copy_file $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf 1 $SUDO_REQUIRED
|
||||
else
|
||||
while true; do
|
||||
echo " [31mReally copy bitcoin.conf with pruning option?[0m"
|
||||
read -p " [31mThis will discard some blockchain data. (yn)[0m " yn
|
||||
case $yn in
|
||||
[Yy]* ) copy_file $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED; break;;
|
||||
[Nn]* ) copy_file $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
[Yy]* ) copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf 1 $SUDO_REQUIRED
|
||||
break;;
|
||||
[Nn]* ) copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
echo " [31mYour cyphernode installation is most likely broken.[0m"
|
||||
echo " [31mPlease check bitcoin.conf.cyphernode on how to repair it manually.[0m";
|
||||
break;;
|
||||
@@ -422,7 +438,8 @@ install_docker() {
|
||||
done
|
||||
fi
|
||||
elif [[ $cmpStatus == 'incompatible' ]]; then
|
||||
copy_file $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf.cyphernode 0 $SUDO_REQUIRED
|
||||
echo " [31mBlockchain data is not compatible, due to misconfigured nets.[0m"
|
||||
echo " [31mYour cyphernode installation is most likely broken.[0m"
|
||||
echo " [31mPlease check bitcoin.conf.cyphernode on how to repair it manually.[0m"
|
||||
@@ -430,7 +447,8 @@ install_docker() {
|
||||
if [[ $cmpStatus == 'reindex' ]]; then
|
||||
echo " [33mWarning[0m Reindexing will take some time."
|
||||
fi
|
||||
copy_file $current_path/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf 1 $SUDO_REQUIRED
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -441,15 +459,15 @@ install_docker() {
|
||||
if [[ $archpath == "rpi" ]]; then
|
||||
dockerfile="Dockerfile-alpine"
|
||||
fi
|
||||
|
||||
if [ ! -d $LIGHTNING_DATAPATH ]; then
|
||||
step " [32mcreate[0m $LIGHTNING_DATAPATH"
|
||||
sudo_if_required mkdir -p $LIGHTNING_DATAPATH
|
||||
next
|
||||
fi
|
||||
if [ -d $LIGHTNING_DATAPATH ]; then
|
||||
copy_file $current_path/lightning/c-lightning/config $LIGHTNING_DATAPATH/config 1 $SUDO_REQUIRED
|
||||
copy_file $current_path/lightning/c-lightning/bitcoin.conf $LIGHTNING_DATAPATH/bitcoin.conf 1 $SUDO_REQUIRED
|
||||
fi
|
||||
|
||||
copy_file $cyphernodeconf_filepath/lightning/c-lightning/config $LIGHTNING_DATAPATH/config 1 $SUDO_REQUIRED
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -496,10 +514,37 @@ install_docker() {
|
||||
fi
|
||||
fi
|
||||
|
||||
copy_file $current_path/installer/docker/docker-compose.yaml $current_path/docker-compose.yaml
|
||||
copy_file $current_path/installer/testfeatures.sh $current_path/testfeatures.sh 0
|
||||
copy_file $current_path/installer/start.sh $current_path/start.sh 0
|
||||
copy_file $current_path/installer/stop.sh $current_path/stop.sh 0
|
||||
local appsnet_entry=$(docker network ls | grep cyphernodeappsnet);
|
||||
|
||||
if [[ $appsnet_entry =~ 'cyphernodeappsnet' ]]; then
|
||||
if [[ $appsnet_entry =~ 'local' && $DOCKER_MODE == 'swarm' ]]; then
|
||||
step " [32mrecreate[0m cyphernode apps network"
|
||||
try docker network rm cyphernodeappsnet > /dev/null 2>&1
|
||||
try docker network create -d overlay --attachable --opt encrypted cyphernodeappsnet > /dev/null 2>&1
|
||||
next
|
||||
elif [[ $appsnet_entry =~ 'swarm' && $DOCKER_MODE == 'compose' ]]; then
|
||||
step " [32mrecreate[0m cyphernode apps network"
|
||||
try docker network rm cyphernodeappsnet > /dev/null 2>&1
|
||||
try docker network create cyphernodeappsnet > /dev/null 2>&1
|
||||
next
|
||||
fi
|
||||
else
|
||||
if [[ $DOCKER_MODE == 'swarm' ]]; then
|
||||
step " [32mcreate[0m cyphernode apps network"
|
||||
try docker network create -d overlay --attachable --opt encrypted cyphernodeappsnet > /dev/null 2>&1
|
||||
next
|
||||
elif [[ $DOCKER_MODE == 'compose' ]]; then
|
||||
step " [32mcreate[0m cyphernode apps network"
|
||||
try docker network create cyphernodeappsnet > /dev/null 2>&1
|
||||
next
|
||||
fi
|
||||
fi
|
||||
|
||||
copy_file $cyphernodeconf_filepath/installer/docker/docker-compose.yaml $current_path/docker-compose.yaml
|
||||
copy_file $cyphernodeconf_filepath/installer/testfeatures.sh $current_path/testfeatures.sh 0
|
||||
copy_file $cyphernodeconf_filepath/installer/start.sh $current_path/start.sh 0
|
||||
copy_file $cyphernodeconf_filepath/installer/stop.sh $current_path/stop.sh 0
|
||||
copy_file $cyphernodeconf_filepath/installer/testdeployment.sh $current_path/testdeployment.sh 0
|
||||
|
||||
if [[ ! -x $current_path/start.sh ]]; then
|
||||
step " [32mmake[0m start.sh executable"
|
||||
@@ -518,14 +563,24 @@ install_docker() {
|
||||
try chmod +x $current_path/testfeatures.sh
|
||||
next
|
||||
fi
|
||||
|
||||
if [[ ! -x $current_path/testdeployment.sh ]]; then
|
||||
step " [32mmake[0m testdeployment.sh executable"
|
||||
try chmod +x $current_path/testdeployment.sh
|
||||
next
|
||||
fi
|
||||
}
|
||||
|
||||
check_directory_owner() {
|
||||
# if one directory does not have access rights for $RUN_AS_USER, we echo 1, else we echo 0
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH")
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$TRAEFIK_DATAPATH")
|
||||
local status=0
|
||||
for d in "${directories[@]}"
|
||||
do
|
||||
if [[ ''$d == '' ]]; then
|
||||
continue
|
||||
fi
|
||||
d=$(realpath $d)
|
||||
if [[ -e $d ]]; then
|
||||
# is it mine and does it have rw ?
|
||||
# don't care about group rights
|
||||
@@ -551,19 +606,38 @@ check_bitcoind() {
|
||||
echo 0
|
||||
}
|
||||
|
||||
sanity_checks() {
|
||||
realpath() {
|
||||
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
|
||||
}
|
||||
|
||||
echo " [32mcheck[0m requirements."
|
||||
|
||||
check_docker() {
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
echo " [31mdocker is not installed on your system. Please check https://www.docker.com/get-started.[0m"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $DOCKER_MODE == 'compose' && ! -x "$(command -v docker-compose)" ]]; then
|
||||
check_docker_compose() {
|
||||
if ! [ -x "$(command -v docker-compose)" ]; then
|
||||
echo " [31mdocker-compose is not installed on your system. Please check https://docs.docker.com/compose/install/.[0m"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
sanity_checks_pre_config() {
|
||||
echo " [32mcheck[0m requirements for configuration step."
|
||||
check_docker
|
||||
}
|
||||
|
||||
sanity_checks_pre_install() {
|
||||
|
||||
echo " [32mcheck[0m requirements for installation step."
|
||||
|
||||
check_docker
|
||||
if [[ $DOCKER_MODE == 'compose' ]]; then
|
||||
check_docker_compose
|
||||
fi
|
||||
|
||||
local OS=$(uname -s)
|
||||
|
||||
@@ -603,7 +677,7 @@ sanity_checks() {
|
||||
if [[ $sudo_reason == 'directories' ]]; then
|
||||
echo " [31mor check your data volumes if they have the right owner.[0m"
|
||||
echo " [31mThe owner of the following folders should be '$RUN_AS_USER':[0m"
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH")
|
||||
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$TRAEFIK_DATAPATH")
|
||||
local status=0
|
||||
for d in "${directories[@]}"
|
||||
do
|
||||
@@ -622,6 +696,14 @@ sanity_checks() {
|
||||
fi
|
||||
}
|
||||
|
||||
install_apps() {
|
||||
if [ ! -d "$current_path/apps" ]; then
|
||||
local apps_repo="https://github.com/SatoshiPortal/cypherapps.git"
|
||||
echo " [32mclone[0m $apps_repo into apps"
|
||||
docker run --rm -v "$current_path":/git --entrypoint git cyphernode/cyphernodeconf:$CONF_VERSION clone --single-branch -b ${CYPHERAPPS_VERSION} "$apps_repo" /git/apps > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
if [[ ''$INSTALLER_MODE == 'none' ]]; then
|
||||
echo "Skipping installation phase"
|
||||
@@ -638,16 +720,20 @@ ALWAYSYES=0
|
||||
SUDO_REQUIRED=0
|
||||
AUTOSTART=0
|
||||
|
||||
# CYPHERNODE VERSION "v0.1.0-rc.2"
|
||||
VERSION_OVERRIDE="true"
|
||||
CONF_VERSION="v0.1-rc.2"
|
||||
GATEKEEPER_VERSION="v0.1-rc.2"
|
||||
PROXY_VERSION="v0.1-rc.2"
|
||||
PROXYCRON_VERSION="v0.1-rc.2"
|
||||
OTSCLIENT_VERSION="v0.1-rc.2"
|
||||
PYCOIN_VERSION="v0.1-rc.2"
|
||||
BITCOIN_VERSION="v0.17.0"
|
||||
LIGHTNING_VERSION="v0.6.2"
|
||||
# CYPHERNODE VERSION "v0.2.4"
|
||||
SETUP_VERSION="v0.2.4"
|
||||
CONF_VERSION="v0.2.4"
|
||||
GATEKEEPER_VERSION="v0.2.4"
|
||||
PROXY_VERSION="v0.2.4"
|
||||
NOTIFIER_VERSION="v0.2.4"
|
||||
PROXYCRON_VERSION="v0.2.4"
|
||||
OTSCLIENT_VERSION="v0.2.4"
|
||||
PYCOIN_VERSION="v0.2.4"
|
||||
CYPHERAPPS_VERSION="v0.2.2"
|
||||
BITCOIN_VERSION="v0.18.0"
|
||||
LIGHTNING_VERSION="v0.7.1"
|
||||
|
||||
SETUP_DIR=$(dirname $(realpath $0))
|
||||
|
||||
# trap ctrl-c and call ctrl_c()
|
||||
trap ctrl_c INT
|
||||
@@ -658,6 +744,7 @@ function ctrl_c() {
|
||||
}
|
||||
|
||||
export current_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
export cyphernodeconf_filepath="$current_path/.cyphernodeconf"
|
||||
|
||||
while getopts ":cirhys" opt; do
|
||||
case $opt in
|
||||
@@ -690,17 +777,34 @@ while getopts ":cirhys" opt; do
|
||||
esac
|
||||
done
|
||||
|
||||
nbbuiltimgs=$(docker images --filter=reference='cyphernode/*:*-local' | wc -l)
|
||||
if [[ $nbbuiltimgs -gt 1 ]]; then
|
||||
read -p "Locally built Cyphernode images found! Do you want to use them? [yn] " -n 1 -r
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
CONF_VERSION="$CONF_VERSION-local"
|
||||
GATEKEEPER_VERSION="$GATEKEEPER_VERSION-local"
|
||||
PROXY_VERSION="$PROXY_VERSION-local"
|
||||
NOTIFIER_VERSION="$NOTIFIER_VERSION-local"
|
||||
PROXYCRON_VERSION="$PROXYCRON_VERSION-local"
|
||||
OTSCLIENT_VERSION="$OTSCLIENT_VERSION-local"
|
||||
PYCOIN_VERSION="$PYCOIN_VERSION-local"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $CONFIGURE == 0 && $INSTALL == 0 && $RECREATE == 0 ]]; then
|
||||
CONFIGURE=1
|
||||
INSTALL=1
|
||||
fi
|
||||
|
||||
if [[ $CONFIGURE == 1 ]]; then
|
||||
sanity_checks_pre_config
|
||||
configure $RECREATE
|
||||
fi
|
||||
|
||||
if [[ -f $current_path/installer/config.sh ]]; then
|
||||
. $current_path/installer/config.sh
|
||||
|
||||
if [[ -f "$cyphernodeconf_filepath/installer/config.sh" ]]; then
|
||||
. "$cyphernodeconf_filepath/installer/config.sh"
|
||||
fi
|
||||
|
||||
if [[ $CLEANUP == 'true' && $(docker image ls | grep cyphernodeconf) =~ cyphernodeconf ]]; then
|
||||
@@ -710,15 +814,18 @@ if [[ $CLEANUP == 'true' && $(docker image ls | grep cyphernodeconf) =~ cypherno
|
||||
fi
|
||||
|
||||
if [[ $INSTALL == 1 ]]; then
|
||||
sanity_checks
|
||||
sanity_checks_pre_install
|
||||
create_user
|
||||
install
|
||||
modify_owner
|
||||
modify_permissions
|
||||
install_apps
|
||||
if [[ ! $AUTOSTART == 1 ]]; then
|
||||
cowsay
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ $AUTOSTART == 1 ]]; then
|
||||
exec $current_path/start.sh
|
||||
else
|
||||
cowsay
|
||||
fi
|
||||
|
||||
@@ -71,6 +71,169 @@ Proxy response:
|
||||
}
|
||||
```
|
||||
|
||||
### Watch a Bitcoin xpub/ypub/zpub/tpub/upub/vpub extended public key (called by application)
|
||||
|
||||
Used to watch the transactions related to an xpub. It will first derive 100 addresses using the provided xpub, derivation path and index information. It will add those addresses to the watching DB table and add those addresses to the Watching-by-xpub wallet. The watching process will take care of calling the provided callbacks when a transaction occurs. When a transaction is seen, Cyphernode will derive and start watching new addresses related to the xpub, keeping a 100 address gap between the last used address in a transaction and the last watched address of that xpub. The label can be used later, instead of the whole xpub, with unwatchxpub* and and getactivewatchesby*.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/watchxpub
|
||||
with body...
|
||||
{"label":"4421","pub32":"upub57Wa4MvRPNyAhxr578mQUdPr6MHwpg3Su875hj8K75AeUVZLXtFeiP52BrhNqDg93gjALU1MMh5UPRiiQPrwiTiuBBBRHzeyBMgrbwkmmkq","path":"0/1/n","nstart":109,"unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id":"5",
|
||||
"event":"watchxpub",
|
||||
"pub32":"upub57Wa4MvRPNyAhxr578mQUdPr6MHwpg3Su875hj8K75AeUVZLXtFeiP52BrhNqDg93gjALU1MMh5UPRiiQPrwiTiuBBBRHzeyBMgrbwkmmkq",
|
||||
"label":"2219",
|
||||
"path":"0/1/n",
|
||||
"nstart":"109",
|
||||
"unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf",
|
||||
"confirmedCallbackURL":"192.168.111.233:1111/callback1conf"
|
||||
}
|
||||
```
|
||||
|
||||
### Un-watch a previously watched Bitcoin xpub by providing the xpub (called by application)
|
||||
|
||||
Updates the watched address rows in DB so that callbacks won't be called on tx confirmations for the provided xpub and related addresses.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/unwatchxpubbyxpub/upub57Wa4MvRPNyAhxr578mQUdPr6MHwpg3Su875hj8K75AeUVZLXtFeiP52BrhNqDg93gjALU1MMh5UPRiiQPrwiTiuBBBRHzeyBMgrbwkmmkq
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"event":"unwatchxpubbyxpub",
|
||||
"pub32":"upub57Wa4MvRPNyAhxr578mQUdPr6MHwpg3Su875hj8K75AeUVZLXtFeiP52BrhNqDg93gjALU1MMh5UPRiiQPrwiTiuBBBRHzeyBMgrbwkmmkq"
|
||||
}
|
||||
```
|
||||
|
||||
### Un-watch a previously watched Bitcoin xpub by providing the label (called by application)
|
||||
|
||||
Updates the watched address rows in DB so that callbacks won't be called on tx confirmations for the provided xpub and related addresses.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/unwatchxpubbylabel/4421
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"event":"unwatchxpubbylabel",
|
||||
"label":"4421"
|
||||
}
|
||||
```
|
||||
|
||||
### Watch a TXID (called by application)
|
||||
|
||||
Used to watch a transaction. Will call the 1-conf callback url after the transaction has been mined. Will call the x-conf callback url after the transaction has x confirmations.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/watchtxid
|
||||
with body...
|
||||
{"txid":"b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","xconfCallbackURL":"192.168.111.233:1111/callbackXconf","nbxconf":6}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id":"5",
|
||||
"event":"watchtxid",
|
||||
"inserted":"1",
|
||||
"txid":"b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387",
|
||||
"confirmedCallbackURL":"192.168.111.233:1111/callback1conf",
|
||||
"xconfCallbackURL":"192.168.111.233:1111/callbackXconf",
|
||||
"nbxconf":6
|
||||
}
|
||||
```
|
||||
|
||||
### Get a list of Bitcoin xpub being watched (called by application)
|
||||
|
||||
Returns the list of currently watched xpub and callback information.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getactivexpubwatches
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"watches": [
|
||||
{
|
||||
"id":"291",
|
||||
"pub32":"upub57Wa4MvRPNyAhxr578mQUdPr6MHwpg3Su875hj8K75AeUVZLXtFeiP52BrhNqDg93gjALU1MMh5UPRiiQPrwiTiuBBBRHzeyBMgrbwkmmkq",
|
||||
"label":"2217",
|
||||
"derivation_path":"1/3/n",
|
||||
"last_imported_n":"121",
|
||||
"unconfirmedCallbackURL":"192.168.133.233:1111/callback0conf",
|
||||
"confirmedCallbackURL":"192.168.133.233:1111/callback1conf",
|
||||
"watching_since":"2018-09-06 21:14:03"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get a list of Bitcoin addresses being watched by provided xpub (called by application)
|
||||
|
||||
Returns the list of currently watched addresses related to the provided xpub and callback information.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getactivewatchesbyxpub/tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"watches": [
|
||||
{
|
||||
"id":"291",
|
||||
"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp",
|
||||
"imported":"1",
|
||||
"unconfirmedCallbackURL":"192.168.133.233:1111/callback0conf",
|
||||
"confirmedCallbackURL":"192.168.133.233:1111/callback1conf",
|
||||
"watching_since":"2018-09-06 21:14:03",
|
||||
"derivation_path":"1/0/n",
|
||||
"pub32_index":"44"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get a list of Bitcoin addresses being watched by provided xpub label (called by application)
|
||||
|
||||
Returns the list of currently watched addresses related to the provided xpub label and callback information.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getactivewatchesbylabel/2219
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"watches": [
|
||||
{
|
||||
"id":"291",
|
||||
"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp",
|
||||
"imported":"1",
|
||||
"unconfirmedCallbackURL":"192.168.133.233:1111/callback0conf",
|
||||
"confirmedCallbackURL":"192.168.133.233:1111/callback1conf",
|
||||
"watching_since":"2018-09-06 21:14:03",
|
||||
"derivation_path":"1/0/n",
|
||||
"pub32_index":"44"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Confirm a Transaction on Watched Address (called by Bitcoin node on transaction confirmations)
|
||||
|
||||
Confirms a transaction on an imported address. The Watching Bitcoin node will notify Cyphernode (thanks to walletnotify in bitcoin.conf) by calling this endpoint with txid when a tx is new or updated on an address. If address is still being watched (flag in DB), the corresponding callbacks will be called.
|
||||
@@ -129,6 +292,108 @@ When cyphernode receives a transaction confirmation (/conf endpoint) on a watche
|
||||
}
|
||||
```
|
||||
|
||||
### Get mempool information
|
||||
|
||||
Returns the mempool information of the Bitcoin node.
|
||||
```http
|
||||
GET http://cyphernode:8888/getmempoolinfo
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"size": 25,
|
||||
"bytes": 5462,
|
||||
"usage": 34736,
|
||||
"maxmempool": 64000000,
|
||||
"mempoolminfee": 1e-05,
|
||||
"minrelaytxfee": 1e-05
|
||||
}
|
||||
```
|
||||
|
||||
### Get the blockchain information (called by application)
|
||||
|
||||
Returns the blockchain information of the Bitcoin node. Used for example by the welcome app to get syncing progression.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getblockchaininfo
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"chain": "test",
|
||||
"blocks": 1486864,
|
||||
"headers": 1486864,
|
||||
"bestblockhash": "000000000000002fb99d683e64bbfc2b7ad16f9a425cf7be77b481fb1afa363b",
|
||||
"difficulty": 13971064.71015782,
|
||||
"mediantime": 1554149114,
|
||||
"verificationprogress": 0.9999994536561675,
|
||||
"initialblockdownload": false,
|
||||
"chainwork": "000000000000000000000000000000000000000000000103ceb57a5896f347ce",
|
||||
"size_on_disk": 23647567017,
|
||||
"pruned": false,
|
||||
"softforks": [
|
||||
{
|
||||
"id": "bip34",
|
||||
"version": 2,
|
||||
"reject": {
|
||||
"status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bip66",
|
||||
"version": 3,
|
||||
"reject": {
|
||||
"status": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bip65",
|
||||
"version": 4,
|
||||
"reject": {
|
||||
"status": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"bip9_softforks": {
|
||||
"csv": {
|
||||
"status": "active",
|
||||
"startTime": 1456790400,
|
||||
"timeout": 1493596800,
|
||||
"since": 770112
|
||||
},
|
||||
"segwit": {
|
||||
"status": "active",
|
||||
"startTime": 1462060800,
|
||||
"timeout": 1493596800,
|
||||
"since": 834624
|
||||
}
|
||||
},
|
||||
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||
}
|
||||
```
|
||||
|
||||
### Get the Block Hash from Height (called by application)
|
||||
|
||||
Returns the best block hash matching height provided.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getblockhash/593104
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":"00000000000000000005dc459f0575b17413dbe7685e3e0fd382ed521f1be68b",
|
||||
"error":null,
|
||||
"id":null
|
||||
}
|
||||
```
|
||||
|
||||
### Get the Best Block Hash (called by application)
|
||||
|
||||
Returns the best block hash of the watching Bitcoin node.
|
||||
@@ -325,12 +590,17 @@ Proxy response:
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Get a new Bitcoin address from spending wallet (called by application)
|
||||
|
||||
Calls getnewaddress RPC on the spending wallet. Used to refill the spending wallet from cold wallet (ie Trezor).
|
||||
Calls getnewaddress RPC on the spending wallet. Used to refill the spending wallet from cold wallet (ie Trezor). Will derive the default address type (set in your bitcoin.conf file, p2sh-segwit if not specified) or you can supply the address type like the following examples.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getnewaddress
|
||||
GET http://cyphernode:8888/getnewaddress/bech32
|
||||
GET http://cyphernode:8888/getnewaddress/legacy
|
||||
GET http://cyphernode:8888/getnewaddress/p2sh-segwit
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
@@ -341,6 +611,12 @@ Proxy response:
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"address":"tb1ql7yvh3lmajxmaljsnsu3w8lhwczu963tvjfzpj"
|
||||
}
|
||||
```
|
||||
|
||||
### Spend coins from spending wallet (called by application)
|
||||
|
||||
Calls sendtoaddress RPC on the spending wallet with supplied info.
|
||||
@@ -360,6 +636,29 @@ Proxy response:
|
||||
}
|
||||
```
|
||||
|
||||
### Bump transaction's fees (called by application)
|
||||
|
||||
Calls bumpfee RPC on the spending wallet with supplied info.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/bumpfee
|
||||
with body...
|
||||
{"txid":"af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648","confTarget":4}
|
||||
or...
|
||||
{"txid":"af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"txid": "7c048f43af90315e201ff4dada8796605a2505c7ab54054ba0e9e05cadd9079b",
|
||||
"origfee": 0.00041221,
|
||||
"fee": 0.00068112,
|
||||
"errors": [ "Blabla don't do that" ]
|
||||
}
|
||||
```
|
||||
|
||||
### Add an output to the next batched transaction (called by application)
|
||||
|
||||
Inserts output information in the DB. Used when batchspend is called later.
|
||||
@@ -555,3 +854,231 @@ Proxy response:
|
||||
"address": "tb1q9n8jfwe9qvlgczfxa5n4pe7haarqflzerqfhk9"
|
||||
}
|
||||
```
|
||||
|
||||
### Get your Lightning Network Node connection string, for others to connect to you
|
||||
|
||||
Returns a string containing your LN node connection information.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_getconnectionstring
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"connectstring": "02ffb6242a744143d427cf0962fce182d426609b80034a297ea5879c8c64d326ab@24.25.26.27:9735"
|
||||
}
|
||||
```
|
||||
|
||||
### Connect to a LN node and fund a channel with it
|
||||
|
||||
First, it will connect your LN node to the supplied LN node. Then, it will fund a channel of the provided amount between you two. Cyphernode will call the supplied callback URL when the channel is ready to be used.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ln_connectfund
|
||||
with body...
|
||||
{"peer":"nodeId@ip:port","msatoshi":"100000","callbackUrl":"https://callbackUrl/?channelReady=f3y2c3cvm4uzg2gq"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "success",
|
||||
"txid": "85b8e69733202e126620e7745be9e23a6b544b758145d86848f3e513e6e1ca42",
|
||||
"channel_id": "a459352219deb8e1b6bdc4a3515888569adad8a3023f8b57edeb0bc4d1f77b74"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"result": "failed",
|
||||
"message": "Failed at watching txid"
|
||||
}
|
||||
```
|
||||
|
||||
### Get a previously created Lightning Network invoice by its label
|
||||
|
||||
Returns the invoice corresponding to the supplied label.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_getinvoice/label
|
||||
GET http://cyphernode:8888/ln_getinvoice/koNCcrSvhX3dmyFhW
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"invoices": [
|
||||
{
|
||||
"label": "koNCcrSvhX3dmyFhW",
|
||||
"bolt11": "lntb10n1pw92fk9pp56p6g7nnuhcj63j6wpyquy67wc7xfanhc20d49ta2dzrge2mj3s5qdq9vscnzcqp2rzjqvdcvlvavcc6zdfvcrehhn4ff024s75dfaqyzmzvuxsj2yd3u684v93ylqqqq0sqqqqqqqqpqqqqqzsqqcu6jamf7du64nxtj99x5t6hvy4hlfv8fc8m5j39g8kyzpk5r89s28f93x5jsfnzl8mhtkhqvx2qxehns4ltw7w5h8h7ppdcw8t0uz0wcptztsqg",
|
||||
"payment_hash": "d0748f4e7cbe25a8cb4e0901c26bcec78c9ecef853db52afaa68868cab728c28",
|
||||
"msatoshi": 1000,
|
||||
"status": "paid",
|
||||
"pay_index": 10,
|
||||
"msatoshi_received": 1002,
|
||||
"paid_at": 1549084373,
|
||||
"description": "d11",
|
||||
"expires_at": 1549087957
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Delete a previously created Lightning Network invoice by its label
|
||||
|
||||
Deletes the invoice corresponding to the supplied label if status is unpaid, so that no payment comes in. Returns the invoice corresponding to the supplied label.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_delinvoice/label
|
||||
GET http://cyphernode:8888/ln_delinvoice/koNCcrSvhX3dmyFhW
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "koNCcrSvhX3dmyFhW",
|
||||
"bolt11": "lntb10n1pw92fk9pp56p6g7nnuhcj63j6wpyquy67wc7xfanhc20d49ta2dzrge2mj3s5qdq9vscnzcqp2rzjqvdcvlvavcc6zdfvcrehhn4ff024s75dfaqyzmzvuxsj2yd3u684v93ylqqqq0sqqqqqqqqpqqqqqzsqqcu6jamf7du64nxtj99x5t6hvy4hlfv8fc8m5j39g8kyzpk5r89s28f93x5jsfnzl8mhtkhqvx2qxehns4ltw7w5h8h7ppdcw8t0uz0wcptztsqg",
|
||||
"payment_hash": "d0748f4e7cbe25a8cb4e0901c26bcec78c9ecef853db52afaa68868cab728c28",
|
||||
"msatoshi": 1000,
|
||||
"status": "unpaid",
|
||||
"description": "d11",
|
||||
"expires_at": 1549087957
|
||||
}
|
||||
```
|
||||
|
||||
### Decodes the BOLT11 string of a Lightning Network invoice
|
||||
|
||||
Returns the detailed information of a BOLT11 string of a Lightning Network invoice.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_decodebolt11/bolt11
|
||||
GET http://cyphernode:8888/ln_decodebolt11/lntb1pdca82tpp5gv8mn5jqlj6xztpnt4r472zcyrwf3y2c3cvm4uzg2gqcnj90f83qdp2gf5hgcm0d9hzqnm4w3kx2apqdaexgetjyq3nwvpcxgcqp2g3d86wwdfvyxcz7kce7d3n26d2rw3wf5tzpm2m5fl2z3mm8msa3xk8nv2y32gmzlhwjved980mcmkgq83u9wafq9n4w28amnmwzujgqpmapcr3
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"currency": "tb",
|
||||
"created_at": 1536073035,
|
||||
"expiry": 3600,
|
||||
"payee": "03bb990f43e6a6eccb223288d32fcb91209b12370c0a8bf5cdf4ad7bc11e33f253",
|
||||
"description": "Bitcoin Outlet order #7082",
|
||||
"min_final_cltv_expiry": 10,
|
||||
"payment_hash": "430fb9d240fcb4612c335d475f285820dc9891588e19baf048520189c8af49e2",
|
||||
"signature": "30440220445a7d39cd4b086c0bd6c67cd8cd5a6a86e8b9345883b56e89fa851decfb876202206b1e6c5122a46c5fbba4ccb4a77ef1bb20078f0aeea4059d5ca3f773db85c920"
|
||||
}
|
||||
```
|
||||
|
||||
### Stamp a hash on the Bitcoin blockchain using OTS (called by application)
|
||||
|
||||
Will stamp the supplied hash to the Bitcoin blockchain using OTS. Cyphernode will curl the callback when the OTS stamping is complete.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ots_stamp
|
||||
with body...
|
||||
{"hash":"1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7","callbackUrl":"192.168.111.233:1111/otscallback?id=1234567"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_stamp",
|
||||
"hash": "1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7",
|
||||
"id": "422",
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_stamp",
|
||||
"hash": "1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7",
|
||||
"id": "422",
|
||||
"result": "error",
|
||||
"error": "Error message from OTS client."
|
||||
}
|
||||
```
|
||||
|
||||
### Get the OTS file of the supplied hash
|
||||
|
||||
Returns the binary OTS file of the supplied hash. If stamp is complete, will return a complete OTS file, or an incomplete OTS file otherwise.
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ots_getfile/1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
Binary application/octet-stream content type.
|
||||
|
||||
### Verify an OTS file
|
||||
|
||||
Will verify the supplied OTS file, or verify the local OTS file named after the supplied hash suffixed with .ots.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ots_verify
|
||||
with body...
|
||||
{"hash":"1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7"}
|
||||
or
|
||||
{"hash":"1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7","base64otsfile":"AE9wZW5UaW1lc3RhbXBzAABQcm9vZ...gABYiWDXPXGQEDxNch"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_verify",
|
||||
"hash": "1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7",
|
||||
"result": "success",
|
||||
"message": "Message from OTS client."
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_verify",
|
||||
"hash": "1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7",
|
||||
"result": "pending",
|
||||
"message": "Message from OTS client."
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_verify",
|
||||
"hash": "1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7",
|
||||
"result": "error",
|
||||
"message": "Error message from OTS client."
|
||||
}
|
||||
```
|
||||
|
||||
### Get info an OTS file
|
||||
|
||||
Will return the base64 string of the detailed information of the supplied OTS file, or of the local OTS file named after the supplied hash suffixed with .ots.
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ots_info
|
||||
with body...
|
||||
{"hash":"1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7"}
|
||||
or
|
||||
{"base64otsfile":"AE9wZW5UaW1lc3RhbXBzAABQcm9vZ...gABYiWDXPXGQEDxNch"}
|
||||
or
|
||||
{"hash":"1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7","base64otsfile":"AE9wZW5UaW1lc3RhbXBzAABQcm9vZ...gABYiWDXPXGQEDxNch"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "ots_info",
|
||||
"result": "success",
|
||||
"message": "Base64 string of the information text"
|
||||
}
|
||||
```
|
||||
179
doc/API.v1.md
Normal file
179
doc/API.v1.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Cyphernode
|
||||
|
||||
## API v1 (RESTful)
|
||||
|
||||
### Collections
|
||||
|
||||
#### watchedAddresses
|
||||
|
||||
| Request | Descripton |
|
||||
|---------|------------|
|
||||
| `POST /v1/watchedAddresses` | Create new address watch |
|
||||
| `GET /v1/watchedAddresses` | Get list of watched addresses |
|
||||
| `GET /v1/watchedAddresses/<address>` | Get details of watched address |
|
||||
| `DELETE /v1/watchedAddresses/<address>` | Remove watched address |
|
||||
|
||||
##### POST /v1/watchedAddresses
|
||||
|
||||
Request body
|
||||
```
|
||||
{
|
||||
"address": <string: address>,
|
||||
"callback": <string: url>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 200 - OK
|
||||
```
|
||||
{
|
||||
"id": <int>,
|
||||
"address": <string: address>,
|
||||
"callback": <string: url>",
|
||||
"estimatesmartfee2blocks": <float: bitcoin>,
|
||||
"estimatesmartfee6blocks": <float: bitcoin>,
|
||||
"estimatesmartfee36blocks": <float: bitcoin>,
|
||||
"estimatesmartfee144blocks": <float: bitcoin>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 503 - Resource temporarily unavailable
|
||||
```
|
||||
{
|
||||
"reason": <string: reason>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 403 - Forbidden
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
##### GET /v1/watchedAddresses
|
||||
|
||||
Response body - 200 - OK
|
||||
```
|
||||
[
|
||||
{
|
||||
"id": <int>,
|
||||
"address": <string: address>,
|
||||
"imported": <bool>,
|
||||
"callback": <string: url>,
|
||||
"watching_since": <datetime: ISO8601-UTC>
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
Response body - 503 - Resource temporarily unavailable
|
||||
```
|
||||
{
|
||||
"reason": <string: reason>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 403 - Forbidden
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
##### GET /v1/watchedAddresses/\<address\>
|
||||
|
||||
Response body - 200 - OK
|
||||
```
|
||||
{
|
||||
"id": <int>,
|
||||
"address": <string: address>,
|
||||
"imported": <bool>,
|
||||
"callback": <string: url>,
|
||||
"watching_since": <datetime: ISO8601-UTC>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 503 - Resource temporarily unavailable
|
||||
```
|
||||
{
|
||||
"reason": <string: reason>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 403 - Forbidden
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 404 - Not found
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
##### DELETE /v1/watchedAddresses/\<address\>
|
||||
|
||||
Response body - 200 - OK
|
||||
```
|
||||
{
|
||||
"address": "<address>",
|
||||
"imported": <bool>,
|
||||
"callback": <string: url>,
|
||||
"watching_since": <datetime: ISO8601-UTC>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 503 - Resource temporarily unavailable
|
||||
```
|
||||
{
|
||||
"reason": <string: reason>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 403 - Forbidden
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 404 - Not found
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
##### Asynchronous callbacks
|
||||
|
||||
Request body
|
||||
```
|
||||
{
|
||||
"id": <int> ,
|
||||
"address": <string: address>,
|
||||
"hash": <string: hash>,
|
||||
"vout_n": <int>,
|
||||
"sent_amount": <float: bitcoin>,
|
||||
"confirmations": <int>,
|
||||
"received": <datetime: ISO8601-UTC>,
|
||||
"size": <int: bytes>,
|
||||
"vsize": <int: bytes>,
|
||||
"fees": <float: bitcoin>,
|
||||
"is_replaceable": <bool>,
|
||||
"blockhash": <string: hash>,
|
||||
"blocktime": <int>,
|
||||
"blockheight": <int>
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 200 - OK
|
||||
```
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
Response body - 503 - Resource temporarily unavailable
|
||||
```
|
||||
{
|
||||
"reason": <string: reason>
|
||||
}
|
||||
```
|
||||
|
||||
BIN
doc/CN-Arch.jpg
Normal file
BIN
doc/CN-Arch.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
17
doc/CYPHERAPPS.md
Normal file
17
doc/CYPHERAPPS.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Cyphernode Apps
|
||||
|
||||
We are providing one default Cyphernode application: The Cyphernode Welcome App. It is a simple Golang application that uses the Cyphernode API to get some information about it: the Bitcoin Core syncing progression, the installed components, a link to download the encrypted config file, a link to download the encrypted API ID/keys file and a link to Spark Wallet, if LN is installed.
|
||||
|
||||
We are also providing Spark Wallet as a Cyphernode application. It is a hybrid application, directly using the c-lightning directory instead of only using the Cyphernode API.
|
||||
|
||||
## Concept
|
||||
|
||||
As you already know it, we want Cyphernode to be modular and decoupled. That's why we created a completely separated repository for the Cyphernode Apps: https://github.com/SatoshiPortal/cypherapps
|
||||
|
||||
Cypherapps acts as an indirection layer between Cyphernode and the actual applications. The repo is cloned into the Cyphernode directory during setup, depending on the selected optional features. The corresponding docker images are taken from the Docker hub repositories.
|
||||
|
||||
Separating Cypherapps from Cyphernode allows us to add applications without changing Cyphernode.
|
||||
|
||||
## Examples
|
||||
|
||||
Welcome App: https://github.com/SatoshiPortal/cyphernode_welcome
|
||||
@@ -1,6 +1,6 @@
|
||||
# This README file can be used if you want to install manually. This is the old documentation before there was the installer.
|
||||
|
||||
# Here are the exact steps I did to install cyphernode on a debian server running on x86 arch, as user debian.
|
||||
# Here are the exact steps I did to install cyphernode on a Debian GNU/Linux server running on x86 arch, as user debian.
|
||||
|
||||
## Update server and install git
|
||||
|
||||
@@ -11,10 +11,10 @@ sudo apt-get update ; sudo apt-get upgrade ; sudo apt-get install git
|
||||
## Docker installation: https://docs.docker.com/install/linux/docker-ce/debian/
|
||||
|
||||
```shell
|
||||
sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
|
||||
sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
|
||||
sudo apt-key fingerprint 0EBFCD88
|
||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian \
|
||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian \
|
||||
$(lsb_release -cs) \
|
||||
stable"
|
||||
sudo apt-get update
|
||||
@@ -142,9 +142,9 @@ sudo find ~/btcdata -type d -exec chmod 2775 {} \; ; sudo find ~/btcdata -type f
|
||||
## Test the deployment
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1/ots_stamp
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/ots_stamp
|
||||
```
|
||||
|
||||
If you need the authorization header to copy/paste in another tool:
|
||||
@@ -157,5 +157,5 @@ id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(ech
|
||||
echo "GET /getbestblockinfo" | docker run --rm -i --network=cyphernodenet alpine nc proxy:8888 -
|
||||
echo "GET /getbalance" | docker run --rm -i --network=cyphernodenet alpine nc proxy:8888 -
|
||||
echo "GET /ln_getinfo" | docker run --rm -i --network=cyphernodenet alpine nc proxy:8888 -
|
||||
docker exec -it `docker ps -q -f name=cyphernodestack_cyphernode` curl -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" proxy:8888/derivepubpath
|
||||
docker exec -it `docker ps -q -f name=cyphernode_proxy` curl -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" proxy:8888/derivepubpath
|
||||
```
|
||||
|
||||
@@ -124,9 +124,9 @@ pi@SP-BTC01:~ $ docker network connect cyphernodenet btcnode
|
||||
## Test deployment from outside of the Swarm
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1/ots_stamp
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/ots_stamp
|
||||
```
|
||||
|
||||
If you need the authorization header to copy/paste in another tool:
|
||||
|
||||
@@ -20,8 +20,28 @@ Or you can simply run this magic command to start setup and installation:
|
||||
curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/master/dist/setup.sh -o setup_cyphernode.sh && chmod +x setup_cyphernode.sh && ./setup_cyphernode.sh
|
||||
```
|
||||
|
||||
Note that you can replace "master" in the URL by "dev" or any existing git branch/tag you actually want to install.
|
||||
|
||||
### Build cyphernode yourself
|
||||
|
||||
You can build cyphernode images yourself. The images will have the same name than the ones in the docker hub, with the suffix -local.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SatoshiPortal/cyphernode.git
|
||||
cd cyphernode
|
||||
./build.sh
|
||||
cd dist
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
`setup.sh` will detect locally built images (with suffix `-local`) and ask you if you want to use them when installing cyphernode.
|
||||
|
||||
For full paranoia mode, you can also build yourself all images used by cyphernode but in external repositories by using the `build.sh` script in each of the repo. You can see a list of images cyphernode uses here: https://cloud.docker.com/u/cyphernode/repository/list
|
||||
|
||||
## Upgrading
|
||||
|
||||
To upgrade to the most recent version, just get and run the most recent version of the setup.sh file as described in the previous section. Migration should be taken care by the script.
|
||||
|
||||
Your proxy's database won't be lost. Migration scripts are taking care of automatically migrating the database when starting the proxy.
|
||||
|
||||
```
|
||||
@@ -39,9 +59,9 @@ id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(ech
|
||||
Directly using curl on command line, put your API ID (id=) and API key (k=) in the following commands:
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1/ots_stamp
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/ots_stamp
|
||||
```
|
||||
|
||||
## Manually test your installation directly on the Proxy:
|
||||
|
||||
@@ -25,14 +25,11 @@ We are providing an installer to help you setup Cyphernode.
|
||||
|
||||
#### See [Instructions for installation](INSTALL.md) for automatic install instructions
|
||||
|
||||
All the Docker images used by Cyphernode have been prebuilt for x86 and ARM (RPi) architectures and are hosted on the Docker hub public registry, Cyphernode repository (https://hub.docker.com/u/cyphernode/).
|
||||
All the Docker images used by Cyphernode have been prebuilt for x86, ARM (RPi) and aarch64 (pine64/NODL) architectures and are hosted on the Docker hub public registry, Cyphernode repository (https://hub.docker.com/u/cyphernode/).
|
||||
|
||||
### Build from sources
|
||||
|
||||
However, it is possible for you to build from sources. In that case, please refer to the files INSTALL-MANUALLY.md and INSTALL-MANUAL-STEPS.md.
|
||||
|
||||
#### See [Instructions for manual installation](INSTALL-MANUALLY.md) for manual build and install instructions
|
||||
#### See [Step-by-step detailed instructions](INSTALL-MANUAL-STEPS.md) for real-world copy-paste standard install instructions
|
||||
However, it is possible for you to build from sources. In that case, please refer to the `build.sh` scripts in each of the repositories used by cyphernode (https://cloud.docker.com/u/cyphernode/repository/list). See [Instructions for installation](INSTALL.md).
|
||||
|
||||
# For Your Information
|
||||
|
||||
@@ -42,6 +39,8 @@ Current components in Cyphernode:
|
||||
- Proxy: request handler. Well dispatch authenticated and authorized requests to the right component. Use a SQLite3 database for its tasks.
|
||||
- Proxy Cron: scheduler. Can call the proxy on regular interval for asynchronous tasks like payment notifications on watches, callbacks when OTS files are ready, etc.
|
||||
- Pycoin: Bitcoin keys and addresses tool. Used by Cyphernode to derive addresses from an xPub and a derivation path.
|
||||
- notifier: Handling callbacks used by watchers as well as OTS stamping.
|
||||
- broker: pub/sub mechanism is taken care by the broker to which all subscribers and publishers should register.
|
||||
- Bitcoin: Bitcoin Core node. Cyphernode uses a watching wallet for watchers (no funds) and a spending wallet for spending. Mandatory component, but optionally part of Cyphernode installation, as we can use an already running Bitcoin Core node.
|
||||
- Lightning: optional. C-Lightning node. The LN node will use the Bitcoin node for its tasks.
|
||||
- OTSclient: optional. Used to stamp hashes on the Bitcoin blockchain.
|
||||
@@ -61,18 +60,18 @@ If you decide to have a prune Bitcoin Core node, the fee calculation on incoming
|
||||
|
||||
## Lightning Network
|
||||
|
||||
Currently, the LN functionalities of Cyphernode are very limited. Maybe even hard to use. You can:
|
||||
Currently, basic LN functionalities is offered by Cyphernode. You can:
|
||||
|
||||
- Get information on your LN node: ln_getinfo
|
||||
- Get a Bitcoin address where to send your funds to be used by your LN node: ln_newaddr
|
||||
- Create an invoice, so people can send you payment; the burden of creating a channel/route to you is on the payer: ln_create_invoice
|
||||
- Pay an invoice. You have to have the invoice and your LN node must already be connected to the network: ln_pay
|
||||
|
||||
Basic and crucial functionalities that's missing (you have to manually use lightning-cli on your LN node):
|
||||
|
||||
- Decode a BOLT11 string.
|
||||
- Delete a created invoice to make sure cancelled payments are not accepted.
|
||||
- Get a previously created invoice.
|
||||
- Connect + fund: connects to a peer and fund a channel, all in one call. A callback can be provided to let you know when the channel is ready to use.
|
||||
- Get connection string: to let your user know how to connect to your LN node.
|
||||
- Be notified when a LN payment is received
|
||||
- Connect your node to the LN network
|
||||
- Open/close channels
|
||||
|
||||
## Manually test your installation through the Gatekeeper
|
||||
|
||||
@@ -85,9 +84,9 @@ id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(ech
|
||||
Directly using curl on command line, put your API ID (id=) and API key (k=) in the following commands:
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1/ots_stamp
|
||||
id="001";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbestblockhash
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/getbalance
|
||||
id="003";h64=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo -n "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' -H "Authorization: Bearer $token" -k https://127.0.0.1:2009/v0/ots_stamp
|
||||
```
|
||||
|
||||
|
||||
|
||||
29
doc/UPGRADE.md
Normal file
29
doc/UPGRADE.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Upgrade notes from 0.1 to 0.2, to upgrade manually
|
||||
|
||||
Usually no need to do this since it will be done during setup.sh v0.2.
|
||||
|
||||
1. cd currentInstallation, where setup.sh is located
|
||||
2. ./stop.sh current running cyphernode
|
||||
3. Execute:
|
||||
|
||||
```
|
||||
docker run --rm -it -v "$PWD:/conf" alpine:3.8
|
||||
apk add --no-cache --update jq curl p7zip
|
||||
cd conf
|
||||
7z e config.7z
|
||||
```
|
||||
|
||||
<enter your password>
|
||||
|
||||
```
|
||||
k=$(dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32) && l="kapi_id=\\\"000\\\";kapi_key=\\\"$k\\\";kapi_groups=\\\"stats\\\";eval ugroups_\${kapi_id}=\${kapi_groups};eval ukey_\${kapi_id}=\${kapi_key}" && cat config.json | sed 's/kapi_groups=\\"/kapi_groups=\\"stats,/g' | jq ".gatekeeper_keys.configEntries = [\"$l\"] + .gatekeeper_keys.configEntries" | jq ".gatekeeper_keys.clientInformation = [\"000=$k\"] + .gatekeeper_keys.clientInformation" | jq ".gatekeeper_apiproperties = \"$(curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/v0.2.0/api_auth_docker/api-sample.properties | paste -s -d '\n')\"" > config.json
|
||||
|
||||
7z u config.7z config.json
|
||||
```
|
||||
|
||||
<enter your password>
|
||||
<CTRL-D>
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/v0.2.0/dist/setup.sh -o setup_cyphernode.sh && chmod +x setup_cyphernode.sh && ./setup_cyphernode.sh
|
||||
```
|
||||
117
doc/openapi/openapi-generator-cli.sh
Executable file
117
doc/openapi/openapi-generator-cli.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
generators="ada \
|
||||
ada-server \
|
||||
android \
|
||||
apache2 \
|
||||
apex \
|
||||
aspnetcore \
|
||||
bash \
|
||||
c \
|
||||
clojure \
|
||||
cwiki \
|
||||
cpp-qt5-client \
|
||||
cpp-qt5-qhttpengine-server \
|
||||
cpp-pistache-server \
|
||||
cpp-restbed-server \
|
||||
cpp-restsdk \
|
||||
cpp-tizen \
|
||||
csharp \
|
||||
csharp-refactor \
|
||||
csharp-dotnet2 \
|
||||
csharp-nancyfx \
|
||||
dart \
|
||||
dart-jaguar \
|
||||
eiffel \
|
||||
elixir \
|
||||
elm \
|
||||
erlang-client \
|
||||
erlang-proper \
|
||||
erlang-server \
|
||||
flash \
|
||||
scala-finch \
|
||||
go \
|
||||
go-server \
|
||||
go-gin-server \
|
||||
graphql-schema \
|
||||
graphql-nodejs-express-server \
|
||||
groovy \
|
||||
kotlin \
|
||||
kotlin-server \
|
||||
kotlin-spring \
|
||||
haskell-http-client \
|
||||
haskell \
|
||||
java \
|
||||
jaxrs-cxf-client \
|
||||
java-inflector \
|
||||
java-msf4j \
|
||||
java-pkmst \
|
||||
java-play-framework \
|
||||
java-undertow-server \
|
||||
java-vertx \
|
||||
jaxrs-cxf \
|
||||
jaxrs-cxf-cdi \
|
||||
jaxrs-jersey \
|
||||
jaxrs-resteasy \
|
||||
jaxrs-resteasy-eap \
|
||||
jaxrs-spec \
|
||||
javascript \
|
||||
javascript-flowtyped \
|
||||
javascript-closure-angular \
|
||||
jmeter \
|
||||
lua \
|
||||
mysql-schema \
|
||||
nodejs-server \
|
||||
objc \
|
||||
openapi \
|
||||
openapi-yaml \
|
||||
perl \
|
||||
php \
|
||||
php-laravel \
|
||||
php-lumen \
|
||||
php-slim \
|
||||
php-silex \
|
||||
php-symfony \
|
||||
php-ze-ph \
|
||||
powershell \
|
||||
python \
|
||||
python-flask \
|
||||
python-aiohttp \
|
||||
r \
|
||||
ruby \
|
||||
ruby-on-rails \
|
||||
ruby-sinatra \
|
||||
rust \
|
||||
rust-server \
|
||||
scalatra \
|
||||
scala-akka \
|
||||
scala-httpclient \
|
||||
scala-gatling \
|
||||
scala-lagom-server \
|
||||
scalaz \
|
||||
spring \
|
||||
dynamic-html \
|
||||
html \
|
||||
html2 \
|
||||
swift2-deprecated \
|
||||
swift3-deprecated \
|
||||
swift4 \
|
||||
typescript-angular \
|
||||
typescript-angularjs \
|
||||
typescript-aurelia \
|
||||
typescript-axios \
|
||||
typescript-fetch \
|
||||
typescript-inversify \
|
||||
typescript-jquery \
|
||||
typescript-node \
|
||||
typescript-rxjs"
|
||||
|
||||
[[ $generators =~ (^|[[:space:]])$2($|[[:space:]]) ]] && echo 'Generator found' || (echo "No such generator" && exit)
|
||||
|
||||
|
||||
if [[ ! -e $1 ]]; then
|
||||
mkdir `pwd`/$2
|
||||
fi
|
||||
|
||||
docker run --rm -v `pwd`:/generator openapitools/openapi-generator-cli generate -o /generator/$2 -i /generator/$1 -g $2
|
||||
1907
doc/openapi/v0/cyphernode-api.yaml
Normal file
1907
doc/openapi/v0/cyphernode-api.yaml
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user