`
206 | >
207 | > Pro Tip: you might want to add `--user root` before container name to run the command with unrestricted permissions
208 |
209 | #### Is it possible to keep simverse workspace on my own path?
210 |
211 | > Yes. Look for [`SIMVERSE_WORKSPACE`](_defaults.sh) env var.
212 |
213 | #### Is it possible to keep projects' repos on my own path?
214 |
215 | > Yes. Look for [`SIMVERSE_REPOS`](_defaults.sh) env var.
216 |
217 | #### What is a master bitcoin node?
218 |
219 | > In a cluster of bitcoin nodes we need one which will be special. It will do some privileged tasks like mining or holding
220 | mined coins in its associated wallet. By convention, master bitcoin node is always first bitcoin node created. You can get its hostname
221 | by running `lookup_host 1 role bitcoin`. Simverse supports multiple bitcoin node implementations and master node might be either
222 | btcd or bitcoind "flavor". Raw cli interface might be slightly different, so we try to hide this in our commands like [`onchain_balance`](toolbox/onchain_balance),
223 | [`chain_height`](toolbox/chain_height) or [`earn`](toolbox/earn). If you looked at their implementation you would spot code branches for different bitcoin node flavors.
224 |
225 | #### Where is a faucet?
226 |
227 | > This is not a testnet with faucet so we have to mine coins ourselves. Look for [`FAUCET_ADDR`](_defaults.sh) env var which contains
228 | > pre-generated bitcoin address which will receive all mined coins. During bitcoin node initialization we import this address into
229 | > a wallet (see [`setup-wallet.sh`](recipes/cookbook/scaffold/docker/btcd/home/setup-wallet.sh)).
230 | > This way `btcctl --wallet sendfrom imported ...` can be used to send funds to others in need. We use similar approach when your
231 | > master bitcoin node is bitcoind.
232 | >
233 | > Please look at [`toolbox/fund`](toolbox/fund) script which can be used for convenient funding of lnd wallets. For example run `fund alice 10`
234 | > to send 10 BTC to Alice's lnd node wallet. The script might decide to call [`toolbox/earn`](toolbox/earn) to mine enough coins and wait
235 | > for their maturity.
236 |
237 | #### What is the difference between heterogeneous and hybrid simnets?
238 |
239 | > A homogeneous simnet contains only `btcd` + `lnd` nodes or only `bitcoind` + `c-lightning` nodes.
240 | Generally, it contains only one flavor of bitcoin and one flavor of lightning nodes. Heterogeneous simnet can mix them.
241 | > A hybrid simnet is a simnet with cooperating nodes running in docker and also on the host machine. This is more advanced
242 | developer setup where you want to develop/debug one specific node and have rest of the simnet running "in the background" in docker.
243 |
244 | #### Cool work! How can I send some sats your way?
245 |
246 |
247 |
248 |
249 |
250 | My pubkey starts with 03e24db0
251 |
252 |
253 |
254 | ---
255 |
256 | ## simverse utility reference
257 |
258 | Note that [`sv`](sv) is a convenience symlink to [`simverse`](simverse).
259 |
260 | ##### `> ./sv help`
261 | ```
262 | Simverse v0.5.
263 |
264 | A generator of simnet clusters for lnd and friends.
265 |
266 | Usage: ./sv [command] [args...]
267 |
268 | Commands:
269 |
270 | create create a new simnet based on a recipe
271 | destroy destroy existing named simnet
272 | list list existing simnets
273 | enter enter a named simnet
274 | state perform state operations on a simnet
275 | repos perform operations on code repositories
276 | help this help page
277 | version display version
278 |
279 | Run `./sv help ` for specific usage.
280 | Run `./sv help ` to learn about general concepts.
281 |
282 | Topics: simnet, recipes, workspace, toolbox, aliases.
283 |
284 | Please visit 'https://github.com/darwin/simverse' for further info.
285 | ```
286 |
287 | ##### `> ./sv help simnet`
288 | ```
289 | About simnets
290 |
291 | Simnet is a cluster of bitcoin and lightning nodes talking to each other.
292 | Simnets are used during development to test different scenarios/workflow
293 | where multiple nodes are required.
294 |
295 | Simnets can have different sizes and shapes. They can be heavily parametrized.
296 |
297 | This tool aims to help with simnet creation and maintenance.
298 |
299 | The goal is to:
300 |
301 | * easily generate a simnet based on given parameters
302 | * easily manage state of a simnet (e.g. for future replays)
303 | * somewhat isolate simnet from host machine (via docker)
304 | * provide cross-platform solution (macos, linux)
305 |
306 | Typical simnet folder structure is:
307 |
308 | _workspace/[simnet_name]/
309 | _states/
310 | master/
311 | certs/
312 | lnd-data-alice/
313 | btcd-data-btcd1/
314 | ...
315 | ...
316 | _volumes -> _states/master
317 | aliases/
318 | docker/
319 | helpers/
320 | repos/
321 | toolbox/
322 | dc
323 | docker-compose.yml
324 | tmux
325 |
326 | Feel free to look around. Below we discuss state management.
327 |
328 | Simnet state
329 |
330 | All docker containers have their state mapped to host machine into _volumes folder.
331 | It contains btcd's data directories, lnd's data directories and similar.
332 | When you stop docker containers and later run them again, the state will persist.
333 |
334 | When you look at _volumes you realize that it is just a symlink somewhere into _states directory.
335 | Typically pointing to 'master', which is the default state.
336 |
337 | You can manage states via `./sv state ...`.
338 | Those are just convenience commands to copy/switch _volumes symlink between states.
339 |
340 | This is an advanced feature for people who want to snapshot a state for further rollbacks.
341 | It can be also used for replays during automated testing.
342 |
343 |
344 | A note on hybrid simnets
345 |
346 | By default, simnet is generated in a way that all nodes live inside docker containers
347 | managed by docker-compose. For convenience we map all relevant ports to host machine.
348 | This allows running another node directly on host machine and interact with nodes in
349 | the cluster inside the docker.
350 |
351 | This is expected workflow for someone who wants to develop particular feature and
352 | needs supporting simnet "in the background".
353 |
354 | ```
355 |
356 | ##### `> ./sv help workspace`
357 | ```
358 | About workspace
359 |
360 | Workspace is a working folder where your generated simnets get stored.
361 | By default it is under `_workspace` but you can control it
362 | via SIMVERSE_WORKSPACE environmental variable.
363 |
364 | Each simnet has a name given to it during `./sv create [recipe] [name]` call.
365 | Workspace contains a folder for each simnet named after it.
366 |
367 | You can enter your simnet via `./sv enter [name]`.
368 | ```
369 |
370 | ##### `> ./sv help create`
371 | ```
372 | Usage: ./sv create [-f] [recipe] [name]
373 |
374 | Creates a new simnet with `name` (default) based on `recipe` (default).
375 | On success, prints a path to generated simnet working folder in your workspace.
376 |
377 | Flags:
378 | -f,--force force creation by destroying previous simnet
379 |
380 | Recipe should be name of a script in `recipes` folder. It specifies requested simnet
381 | parameters and drives the generator.
382 |
383 | Read more about recipes via `./sv help recipes`
384 | ```
385 |
386 | ##### `> ./sv help recipe`
387 | ```
388 | About recipes
389 |
390 | Simnets can have different sizes and shapes. They can be heavily parametrized.
391 | Recipe is a script describing how to build a given simnet.
392 |
393 | An example of a simple recipe:
394 |
395 | . cookbook/cookbook.sh
396 |
397 | prelude
398 |
399 | add btcd btcd
400 |
401 | add lnd alice
402 | add lnd bob
403 |
404 | Recipes are located under `recipes` folder.
405 | Say, we stored the above recipe as `recipes/example.sh`.
406 |
407 | By running `./sv create example mysn`, we create a new simnet named `mysn`
408 | which has one btcd node and two lnd nodes, all with default settings.
409 |
410 | Recipes are bash scripts executed as the last step in simnet creation.
411 | That means you can do anything bash can do to tweak given simnet.
412 | To make your life easier, we provide a simple library "cookbook" for building
413 | simnet on step-by-step basis with sane defaults.
414 |
415 | We are not going to document the cookbook here. Please refer to its sources.
416 |
417 | Please look around in `recipes` folder and see how existing recipes are done.
418 | ```
419 |
420 | ##### `> ./sv help toolbox`
421 | ```
422 | About toolbox
423 |
424 | Toolbox is a set of convenience scripts for typical interaction with simnet.
425 |
426 | When you enter a simnet via `./sv enter [name]`, toolbox folder is added to your PATH.
427 |
428 | Explore `toolbox` folder for the details:
429 |
430 | attach_dlv
431 | brief
432 | chain_height
433 | connect
434 | earn
435 | faucet_balance
436 | fund
437 | generate
438 | get_route
439 | inspect_container
440 | list_docker_ips
441 | ln_balance
442 | lookup_container
443 | lookup_service
444 | newaddr
445 | onchain_balance
446 | open_channel
447 | pay
448 | pubkey
449 | req_pay
450 | simnet_ready
451 |
452 | ```
453 |
454 | ##### `> ./sv help aliases`
455 | ```
456 | About aliases
457 |
458 | Aliases are scripts generated depending on shape/parameters of your simnet.
459 |
460 | When you enter a simnet via `./sv enter [name]`. Aliases folder is added to your PATH.
461 |
462 | For example default simnet will generate following aliases for you:
463 |
464 | alice
465 | bob
466 | btcd1
467 | btcctl -> btcd1
468 | lncli -> alice
469 |
470 | Aliases are convenience shortcuts to control tools for individual nodes (named by simnet recipe).
471 |
472 | Additionally there will be generated `btcctl` symlink pointing to first btcd node. And `lncli`
473 | symlink pointing to the first lnd node. This comes handy for asking general questions about networks
474 | not specific to exact node.
475 | ```
476 |
477 | ##### `> ./sv help destroy`
478 | ```
479 | Usage: ./sv destroy [name]
480 |
481 | Deletes a simnet with `name` (default).
482 | ```
483 |
484 | ##### `> ./sv help enter`
485 | ```
486 | Usage: ./sv enter [name]
487 |
488 | Enters into a sub-shell with environment prepared for simnet with `name` (default).
489 |
490 | You typically use this command to start working with a given simnet. In the sub-shell we:
491 |
492 | * switch working directory into simnet's folder
493 | * set PATH to additionally contain toolbox and aliases
494 | ```
495 |
496 | ##### `> ./sv help list`
497 | ```
498 | Usage: ./sv list [filter]
499 |
500 | Lists all available simnets by name. Optionally you can filter the list using a case-insensitive substring.
501 |
502 | Run `./sv help simnet` to learn what is a simnet.
503 | ```
504 |
505 | ##### `> ./sv help state`
506 | ```
507 | Usage: ./sv state [sub-command] ...
508 |
509 | Manipulates simnet state.
510 |
511 | Sub-commands:
512 |
513 | show show currently selected state in a given simnet
514 | clone clone existing state in a given simnet
515 | switch switch selection to a named state in a given simnet
516 | list list states for a given simnet
517 | rm remove a named state in a given simnet
518 |
519 | ./sv state show [simnet_name]
520 | ./sv state clone [--force] [--switch] [simnet_name] [old_state_name]
521 | ./sv state switch [simnet_name] [state_name]
522 | ./sv state list [simnet_name] [filter]
523 | ./sv state rm [simnet_name]
524 |
525 | Run `./sv help simnet` to learn about simnet states.
526 | ```
527 |
528 | ##### `> ./sv help repos`
529 | ```
530 | Usage: ./sv repos [sub-command] ...
531 |
532 | Manipulates code repositories.
533 |
534 | Sub-commands:
535 |
536 | init init default repos (git clone)
537 | update update default repos (git pull)
538 | clone clone existing repo under a new name
539 | list list repos
540 | rm remove repo(s)
541 | report report current tips
542 | unshallow unshallow repos
543 |
544 | ./sv repos init [repo_name] [...]
545 | ./sv repos update [repo_name] [...]
546 | ./sv repos clone [--force]
547 | ./sv repos list [filter]
548 | ./sv repos rm [repo_name] [...]
549 | ./sv repos report [filter]
550 | ./sv repos unshallow [filter]
551 | ```
552 |
--------------------------------------------------------------------------------
/recipes/cookbook/cookbook.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e -o pipefail
4 |
5 | # this script defines a simple DSL for incrementally building docker-compose config and related files
6 |
7 | # recipes must be invoked from $SIMVERSE_HOME/recipes dir, with
8 | #
9 | # also note that SIMVERSE_HOME, SIMVERSE_WORKSPACE and SIMVERSE_REPOS must be set
10 |
11 | SIMNET_NAME=${1:?please specify a simnet name as first argument}
12 |
13 | # example usage:
14 | #
15 | # . cookbook/cookbook.sh
16 | #
17 | # prelude
18 | # add btcd prague
19 | # add lnd alice
20 | # add lnd bob
21 |
22 | REQUIRED="variable not set or empty, (hint: source _defaults.sh)"
23 |
24 | SIMVERSE_HOME=${SIMVERSE_HOME:?$REQUIRED}
25 | SIMVERSE_REPOS=${SIMVERSE_REPOS:?$REQUIRED}
26 | SIMVERSE_WORKSPACE=${SIMVERSE_WORKSPACE:?$REQUIRED}
27 | COOKBOOK_DIR="$SIMVERSE_HOME/recipes/cookbook"
28 | TEMPLATES_DIR="$COOKBOOK_DIR/templates"
29 | SCAFFOLD_DIR="$COOKBOOK_DIR/scaffold"
30 | TOOLBOX_DIR="$SIMVERSE_HOME/toolbox"
31 |
32 | COMPOSE_FILE="docker-compose.yml"
33 | ALIASES_DIR_NAME="aliases"
34 |
35 | # configured externally via env (see _defaults.sh)
36 |
37 | FIRST_DLV_PORT_ON_HOST=${FIRST_DLV_PORT_ON_HOST:?$REQUIRED}
38 | FIRST_DLV_PORT=${FIRST_DLV_PORT:?$REQUIRED}
39 |
40 | FIRST_LND_SERVER_PORT_ON_HOST=${FIRST_LND_SERVER_PORT_ON_HOST:?$REQUIRED}
41 | FIRST_LND_RPC_PORT_ON_HOST=${FIRST_LND_RPC_PORT_ON_HOST:?$REQUIRED}
42 | FIRST_LND_REST_PORT_ON_HOST=${FIRST_LND_REST_PORT_ON_HOST:?$REQUIRED}
43 | FIRST_BTCD_SERVER_PORT_ON_HOST=${FIRST_BTCD_SERVER_PORT_ON_HOST:?$REQUIRED}
44 | FIRST_BTCD_RPC_PORT_ON_HOST=${FIRST_BTCD_RPC_PORT_ON_HOST:?$REQUIRED}
45 | FIRST_BTCWALLET_RPC_PORT_ON_HOST=${FIRST_BTCWALLET_RPC_PORT_ON_HOST:?$REQUIRED}
46 | FIRST_BITCOIND_SERVER_PORT_ON_HOST=${FIRST_BITCOIND_SERVER_PORT_ON_HOST:?$REQUIRED}
47 | FIRST_BITCOIND_RPC_PORT_ON_HOST=${FIRST_BITCOIND_RPC_PORT_ON_HOST:?$REQUIRED}
48 | FIRST_LIGHTNING_SERVER_PORT_ON_HOST=${FIRST_LIGHTNING_SERVER_PORT_ON_HOST:?$REQUIRED}
49 | FIRST_LIGHTNING_RPC_PORT_ON_HOST=${FIRST_LIGHTNING_RPC_PORT_ON_HOST:?$REQUIRED}
50 | FIRST_ECLAIR_SERVER_PORT_ON_HOST=${FIRST_ECLAIR_SERVER_PORT_ON_HOST:?$REQUIRED}
51 | FIRST_ECLAIR_RPC_PORT_ON_HOST=${FIRST_ECLAIR_RPC_PORT_ON_HOST:?$REQUIRED}
52 |
53 | LND_AUTO_NAME_PREFIX=${LND_AUTO_NAME_PREFIX:?$REQUIRED}
54 | BTCD_AUTO_NAME_PREFIX=${BTCD_AUTO_NAME_PREFIX:?$REQUIRED}
55 | BITCOIND_AUTO_NAME_PREFIX=${BITCOIND_AUTO_NAME_PREFIX:?$REQUIRED}
56 | LIGHTNING_AUTO_NAME_PREFIX=${LIGHTNING_AUTO_NAME_PREFIX:?$REQUIRED}
57 | ECLAIR_AUTO_NAME_PREFIX=${ECLAIR_AUTO_NAME_PREFIX:?$REQUIRED}
58 |
59 | DEFAULT_BTCD_REPO_PATH=${DEFAULT_BTCD_REPO_PATH:?$REQUIRED}
60 | DEFAULT_BTCWALLET_REPO_PATH=${DEFAULT_BTCWALLET_REPO_PATH:?$REQUIRED}
61 | DEFAULT_LND_REPO_PATH=${DEFAULT_LND_REPO_PATH:?$REQUIRED}
62 | DEFAULT_BITCOIND_REPO_PATH=${DEFAULT_BITCOIND_REPO_PATH:?$REQUIRED}
63 | DEFAULT_LIGHTNING_REPO_PATH=${DEFAULT_LIGHTNING_REPO_PATH:?$REQUIRED}
64 | DEFAULT_ECLAIR_REPO_PATH=${DEFAULT_ECLAIR_REPO_PATH:?$REQUIRED}
65 |
66 | DEFAULT_BTCD_CONF_PATH=${DEFAULT_BTCD_CONF_PATH:?$REQUIRED}
67 | DEFAULT_BTCWALLET_CONF_PATH=${DEFAULT_BTCWALLET_CONF_PATH:?$REQUIRED}
68 | DEFAULT_LND_CONF_PATH=${DEFAULT_LND_CONF_PATH:?$REQUIRED}
69 | DEFAULT_BITCOIND_CONF_PATH=${DEFAULT_BITCOIND_CONF_PATH:?$REQUIRED}
70 | DEFAULT_LIGHTNING_CONF_PATH=${DEFAULT_LIGHTNING_CONF_PATH:?$REQUIRED}
71 | DEFAULT_ECLAIR_CONF_PATH=${DEFAULT_ECLAIR_CONF_PATH:?$REQUIRED}
72 |
73 | LND_BITCOIN_RPC_HOST=${LND_BITCOIN_RPC_HOST}
74 | LND_BACKEND=${LND_BACKEND}
75 |
76 | LIGHTNING_BITCOIN_RPC_HOST=${LIGHTNING_BITCOIN_RPC_HOST}
77 | LIGHTNING_BACKEND=${LIGHTNING_BACKEND}
78 |
79 | ECLAIR_BITCOIN_RPC_HOST=${ECLAIR_BITCOIN_RPC_HOST}
80 | ECLAIR_BACKEND=${ECLAIR_BACKEND}
81 |
82 | # error codes
83 | ERR_NOT_IMPLEMENTED=10
84 | ERR_MUST_CALL_PRELUDE_FIRST=11
85 | ERR_NEED_BTCD_OR_BITCOIND=12
86 | ERR_REQUIRE_BITCOIND=13
87 |
88 | # -- helpers ----------------------------------------------------------------------------------------------------------------
89 |
90 | echo_err() {
91 | printf "\e[31m%s\e[0m\n" "$*" >&2;
92 | }
93 |
94 | print_stack_trace() {
95 | local i=0
96 | while caller ${i}; do
97 | ((++i))
98 | done
99 | }
100 |
101 | echo_err_with_stack_trace() {
102 | echo_err "$@"
103 | print_stack_trace | tail -n "+2"
104 | }
105 |
106 | # -- utils ------------------------------------------------------------------------------------------------------------------
107 |
108 | init_common_defaults() {
109 | local
110 | }
111 |
112 | # shellcheck disable=SC2034
113 | init_btcd_defaults() {
114 | BTCD_REPO_PATH=${DEFAULT_BTCD_REPO_PATH}
115 | BTCWALLET_REPO_PATH=${DEFAULT_BTCWALLET_REPO_PATH}
116 | BTCD_CONF_PATH=${DEFAULT_BTCD_CONF_PATH}
117 | BTCWALLET_CONF_PATH=${DEFAULT_BTCWALLET_CONF_PATH}
118 | }
119 |
120 | # shellcheck disable=SC2034
121 | init_lnd_defaults() {
122 | LND_REPO_PATH=${DEFAULT_LND_REPO_PATH}
123 | LND_CONF_PATH=${DEFAULT_LND_CONF_PATH}
124 | }
125 |
126 | # shellcheck disable=SC2034
127 | init_bitcoind_defaults() {
128 | BITCOIND_REPO_PATH=${DEFAULT_BITCOIND_REPO_PATH}
129 | BITCOIND_CONF_PATH=${DEFAULT_BITCOIND_CONF_PATH}
130 | }
131 |
132 | # shellcheck disable=SC2034
133 | init_lightning_defaults() {
134 | LIGHTNING_REPO_PATH=${DEFAULT_LIGHTNING_REPO_PATH}
135 | LIGHTNING_CONF_PATH=${DEFAULT_LIGHTNING_CONF_PATH}
136 | }
137 |
138 | # shellcheck disable=SC2034
139 | init_eclair_defaults() {
140 | ECLAIR_REPO_PATH=${DEFAULT_ECLAIR_REPO_PATH}
141 | ECLAIR_CONF_PATH=${DEFAULT_ECLAIR_CONF_PATH}
142 | }
143 |
144 | init_defaults() {
145 | init_common_defaults
146 | init_bitcoind_defaults
147 | init_btcd_defaults
148 | init_lightning_defaults
149 | init_lnd_defaults
150 | init_eclair_defaults
151 | }
152 |
153 | reset_common_counters() {
154 | SERVICE_COUNTER=1
155 | BITCOIN_COUNTER=1
156 | LN_COUNTER=1
157 | DLV_PORT=${FIRST_DLV_PORT}
158 | DLV_PORT_ON_HOST=${FIRST_DLV_PORT_ON_HOST}
159 | LAST_BITCOIN_SERVICE=
160 | }
161 |
162 | reset_btcd_counters() {
163 | LAST_BTCD_SERVICE=
164 | BTCD_COUNTER=1
165 | BTCD_AUTO_NAME_COUNTER=0
166 | BTCD_SERVER_PORT_ON_HOST=${FIRST_BTCD_SERVER_PORT_ON_HOST}
167 | BTCD_RPC_PORT_ON_HOST=${FIRST_BTCD_RPC_PORT_ON_HOST}
168 | BTCWALLET_RPC_PORT_ON_HOST=${FIRST_BTCWALLET_RPC_PORT_ON_HOST}
169 | }
170 |
171 | reset_lnd_counters() {
172 | LND_COUNTER=1
173 | LND_AUTO_NAME_COUNTER=0
174 | LND_SERVER_PORT_ON_HOST=${FIRST_LND_SERVER_PORT_ON_HOST}
175 | LND_RPC_PORT_ON_HOST=${FIRST_LND_RPC_PORT_ON_HOST}
176 | LND_REST_PORT_ON_HOST=${FIRST_LND_REST_PORT_ON_HOST}
177 | }
178 |
179 | reset_bitcoind_counters() {
180 | LAST_BITCOIND_SERVICE=
181 | BITCOIND_COUNTER=1
182 | BITCOIND_AUTO_NAME_COUNTER=0
183 | BITCOIND_SERVER_PORT_ON_HOST=${FIRST_BITCOIND_SERVER_PORT_ON_HOST}
184 | BITCOIND_RPC_PORT_ON_HOST=${FIRST_BITCOIND_RPC_PORT_ON_HOST}
185 | }
186 |
187 | reset_lightning_counters() {
188 | LIGHTNING_COUNTER=1
189 | LIGHTNING_AUTO_NAME_COUNTER=0
190 | LIGHTNING_SERVER_PORT_ON_HOST=${FIRST_LIGHTNING_SERVER_PORT_ON_HOST}
191 | LIGHTNING_RPC_PORT_ON_HOST=${FIRST_LIGHTNING_RPC_PORT_ON_HOST}
192 | }
193 |
194 | reset_eclair_counters() {
195 | ECLAIR_COUNTER=1
196 | ECLAIR_AUTO_NAME_COUNTER=0
197 | ECLAIR_SERVER_PORT_ON_HOST=${FIRST_ECLAIR_SERVER_PORT_ON_HOST}
198 | ECLAIR_RPC_PORT_ON_HOST=${FIRST_ECLAIR_RPC_PORT_ON_HOST}
199 | }
200 |
201 | reset_counters() {
202 | reset_common_counters
203 | reset_lnd_counters
204 | reset_btcd_counters
205 | reset_bitcoind_counters
206 | reset_lightning_counters
207 | reset_eclair_counters
208 | }
209 |
210 | advance_common_counters() {
211 | ((++SERVICE_COUNTER))
212 | ((++DLV_PORT))
213 | ((++DLV_PORT_ON_HOST))
214 | }
215 |
216 | advance_btcd_counters() {
217 | ((++BTCD_COUNTER))
218 | ((++BTCD_SERVER_PORT_ON_HOST))
219 | ((++BTCD_RPC_PORT_ON_HOST))
220 | ((++BTCWALLET_RPC_PORT_ON_HOST))
221 |
222 | ((++BITCOIN_COUNTER))
223 | }
224 |
225 | advance_lnd_counters() {
226 | ((++LND_COUNTER))
227 | ((++LND_SERVER_PORT_ON_HOST))
228 | ((++LND_RPC_PORT_ON_HOST))
229 | ((++LND_REST_PORT_ON_HOST))
230 |
231 | ((++LN_COUNTER))
232 | }
233 |
234 | advance_bitcoind_counters() {
235 | ((++BITCOIND_COUNTER))
236 | ((++BITCOIND_SERVER_PORT_ON_HOST))
237 | ((++BITCOIND_RPC_PORT_ON_HOST))
238 |
239 | ((++BITCOIN_COUNTER))
240 | }
241 |
242 | advance_lightning_counters() {
243 | ((++LIGHTNING_COUNTER))
244 | ((++LIGHTNING_SERVER_PORT_ON_HOST))
245 | ((++LIGHTNING_RPC_PORT_ON_HOST))
246 |
247 | ((++LN_COUNTER))
248 | }
249 |
250 | advance_eclair_counters() {
251 | ((++ECLAIR_COUNTER))
252 | ((++ECLAIR_SERVER_PORT_ON_HOST))
253 | ((++ECLAIR_RPC_PORT_ON_HOST))
254 |
255 | ((++LN_COUNTER))
256 | }
257 |
258 | gen_lnd_auto_name() {
259 | echo "${LND_AUTO_NAME_PREFIX}${LND_AUTO_NAME_COUNTER}"
260 | }
261 |
262 | advance_lnd_auto_name_counter() {
263 | ((++LND_AUTO_NAME_COUNTER))
264 | }
265 |
266 | gen_btcd_auto_name() {
267 | echo "${BTCD_AUTO_NAME_PREFIX}${BTCD_AUTO_NAME_COUNTER}"
268 | }
269 |
270 | advance_btcd_auto_name_counter() {
271 | ((++BTCD_AUTO_NAME_COUNTER))
272 | }
273 |
274 | gen_bitcoind_auto_name() {
275 | echo "${BITCOIND_AUTO_NAME_PREFIX}${BITCOIND_AUTO_NAME_COUNTER}"
276 | }
277 |
278 | advance_bitcoind_auto_name_counter() {
279 | ((++BITCOIND_AUTO_NAME_COUNTER))
280 | }
281 |
282 | gen_lightning_auto_name() {
283 | echo "${LIGHTNING_AUTO_NAME_PREFIX}${LIGHTNING_AUTO_NAME_COUNTER}"
284 | }
285 |
286 | advance_lightning_auto_name_counter() {
287 | ((++LIGHTNING_AUTO_NAME_COUNTER))
288 | }
289 |
290 | gen_eclair_auto_name() {
291 | echo "${ECLAIR_AUTO_NAME_PREFIX}${ECLAIR_AUTO_NAME_COUNTER}"
292 | }
293 |
294 | advance_eclair_auto_name_counter() {
295 | ((++ECLAIR_AUTO_NAME_COUNTER))
296 | }
297 |
298 | eval_template() {
299 | local template_file=$1
300 | eval "cat < /dev/null
304 | }
305 |
306 | dolarize() {
307 | # in template scripts we use double dollar convention for template parameters
308 | sed 's/\$/>##<>#$/g' | sed 's/`/>###$/g' | sed 's/>##`/g'
314 | }
315 |
316 | eval_script_template() {
317 | local template_file=$1
318 | eval "cat < /dev/null
322 | }
323 |
324 | into_dockerfile_snippet() {
325 | # in dockerfile template scripts we use '#include ' convention for template parameters
326 | sed 's/\$/>###BACKSLASH#$/g' | sed 's/>##`/g' | sed 's/>BACKSLASH\\/g'
332 | }
333 |
334 | eval_dockerfile_template() {
335 | local template_file=$1
336 | eval "cat < /dev/null
340 | }
341 |
342 | create_aliases_dir() {
343 | mkdir "$ALIASES_DIR_NAME"
344 | cp "$TEMPLATES_DIR/_alias_utils.sh" "$ALIASES_DIR_NAME"
345 | }
346 |
347 | prepare_repos() {
348 | # the problem is that our docker build context is somewhere $SIMVERSE_HOME/_workspace/[simnetname] (we can have multiple)
349 | # but we need to access files from $SIMVERSE_HOME/_repos
350 | # due to security reasons docker does not allow to access files outside that build context
351 | # here we attempt a fast way how to mount _repos as repos inside the docker context
352 | case "$OSTYPE" in
353 | darwin*)
354 | # -c assumes fast APFS clone
355 | cp -c -r "$SIMVERSE_REPOS" repos
356 | ;;
357 | linux*)
358 | # TODO: see https://superuser.com/a/842690
359 | # mkdir -p repos
360 | # sudo mount --bind "$SIMVERSE_REPOS" repos
361 |
362 | # use slower cp for now...
363 | cp -r "$SIMVERSE_REPOS" repos
364 | ;;
365 | *)
366 | echo "NOT IMPLEMENTED: add support for mirroring repos inside docker context for $OSTYPE"
367 | exit ${ERR_NOT_IMPLEMENTED}
368 | ;;
369 | esac
370 | }
371 |
372 | interpolate_dockerfile() {
373 | local path=$1
374 | eval_dockerfile_template "$SCAFFOLD_DIR/docker/$path" > "docker/$path"
375 | }
376 |
377 | # shellcheck disable=SC2034
378 | generate_dockerfiles() {
379 | BASE_DOCKERFILE_SNIPPET=$(eval_dockerfile_template "$TEMPLATES_DIR/docker/base/Dockerfile")
380 | BUILDTIME_DOCKERFILE_SNIPPET=$(eval_dockerfile_template "$TEMPLATES_DIR/docker/buildtime/Dockerfile")
381 | RUNTIME_DOCKERFILE_SNIPPET=$(eval_dockerfile_template "$TEMPLATES_DIR/docker/runtime/Dockerfile")
382 |
383 | interpolate_dockerfile "bitcoind/Dockerfile"
384 | interpolate_dockerfile "btcd/Dockerfile"
385 | interpolate_dockerfile "eclair/Dockerfile"
386 | interpolate_dockerfile "lightning/Dockerfile"
387 | interpolate_dockerfile "lnd/Dockerfile"
388 | interpolate_dockerfile "pre/Dockerfile"
389 | }
390 |
391 | scaffold_simnet() {
392 | # copy including dot files
393 | shopt -s dotglob
394 | cp -aL "$SCAFFOLD_DIR"/* .
395 | shopt -u dotglob
396 | generate_dockerfiles
397 | }
398 |
399 | add_toolbox() {
400 | ln -s "$TOOLBOX_DIR" toolbox
401 |
402 | }
403 |
404 | init_states() {
405 | mkdir "_states"
406 | mkdir "_states/master"
407 | ln -s "_states/master" _volumes
408 | }
409 |
410 | prepare_tmux_script() {
411 | eval_script_template "$TEMPLATES_DIR/tmux" > "tmux"
412 | chmod +x "tmux"
413 | }
414 |
415 | prepare_home_link() {
416 | ln -s "$SIMVERSE_HOME" home
417 | }
418 |
419 | echo_service_separator() {
420 | local kind=$1
421 | local name=$2
422 | local counter=$3
423 | echo -e "\n # -- ${counter}. $kind service -----------------------------------------------------------" >> ${COMPOSE_FILE}
424 | }
425 |
426 | # we have to create stub folders for volumes on host
427 | # if we let docker container create them instead under some systems we could end up with root permissions on them
428 |
429 | prepare_pre_volumes() {
430 | mkdir _volumes/certs
431 | }
432 |
433 | prepare_btcd_volumes() {
434 | local name=${1:?required}
435 | mkdir "_volumes/btcd-data-${name}"
436 | mkdir "_volumes/btcwallet-data-${name}"
437 | }
438 |
439 | prepare_lnd_volumes() {
440 | local name=${1:?required}
441 | mkdir "_volumes/lnd-data-${name}"
442 | }
443 |
444 | prepare_bitcoind_volumes() {
445 | local name=${1:?required}
446 | mkdir "_volumes/bitcoind-data-${name}"
447 | }
448 |
449 | prepare_lightning_volumes() {
450 | local name=${1:?required}
451 | mkdir "_volumes/lightning-data-${name}"
452 | }
453 |
454 | prepare_eclair_volumes() {
455 | local name=${1:?required}
456 | mkdir "_volumes/eclair-data-${name}"
457 | }
458 |
459 | ensure_bitcoin_service() {
460 | if [[ -z "$LAST_BITCOIN_SERVICE" ]]; then
461 | echo_err_with_stack_trace "'add lnd' called but no prior btcd or bitcoind was added, call 'add btcd' or 'add bitcoind' prior adding lnd nodes or set LND_BITCOIN_RPC_HOST explicitly"
462 | exit ${ERR_NEED_BTCD_OR_BITCOIND}
463 | fi
464 | }
465 |
466 | get_last_bitcoin_service() {
467 | ensure_bitcoin_service
468 | echo "$LAST_BITCOIN_SERVICE"
469 | }
470 |
471 | get_last_bitcoin_backend() {
472 | ensure_bitcoin_service
473 | if [[ "$LAST_BITCOIN_SERVICE" == "$LAST_BITCOIND_SERVICE" ]]; then
474 | echo "bitcoind"
475 | return
476 | fi
477 | echo "btcd"
478 | }
479 |
480 | add_lnd() {
481 | NAME=$1
482 |
483 | # generate default name if not given
484 | if [[ -z "$NAME" ]]; then
485 | advance_lnd_auto_name_counter
486 | NAME=$(gen_lnd_auto_name)
487 | fi
488 |
489 | # auto-provide LND_BITCOIN_RPC_HOST if not given
490 | local prev_lnd_bitcoin_rpc_host="$LND_BITCOIN_RPC_HOST"
491 | if [[ -z "$LND_BITCOIN_RPC_HOST" ]]; then
492 | LND_BITCOIN_RPC_HOST="$(get_last_bitcoin_service)"
493 | fi
494 | local prev_lnd_backend="$LND_BACKEND"
495 | if [[ -z "$LND_BACKEND" ]]; then
496 | LND_BACKEND="$(get_last_bitcoin_backend)"
497 | fi
498 |
499 | echo_service_separator lnd "${NAME}" ${LND_COUNTER}
500 | eval_template "$TEMPLATES_DIR/lnd.yml" >> ${COMPOSE_FILE}
501 |
502 | local alias_file="$ALIASES_DIR_NAME/$NAME"
503 | eval_script_template "$TEMPLATES_DIR/lncli-alias.sh" >> "$alias_file"
504 | chmod +x "$alias_file"
505 |
506 | # point generic lncli to first lnd node
507 | local default_alias_file="$ALIASES_DIR_NAME/lncli"
508 | if [[ ! -f "$default_alias_file" ]]; then
509 | ln -s "$NAME" ${default_alias_file}
510 | fi
511 |
512 | prepare_lnd_volumes "$NAME"
513 |
514 | advance_common_counters
515 | advance_lnd_counters
516 |
517 | LND_BITCOIN_RPC_HOST="$prev_lnd_bitcoin_rpc_host"
518 | LND_BACKEND="$prev_lnd_backend"
519 | }
520 |
521 | add_btcd() {
522 | NAME=$1
523 |
524 | # generate default name if not given
525 | if [[ -z "$NAME" ]]; then
526 | advance_btcd_auto_name_counter
527 | NAME=$(gen_btcd_auto_name)
528 | fi
529 |
530 | echo_service_separator btcd "${NAME}" ${BTCD_COUNTER}
531 | eval_template "$TEMPLATES_DIR/btcd.yml" >> ${COMPOSE_FILE}
532 |
533 | local alias_file="$ALIASES_DIR_NAME/$NAME"
534 | eval_script_template "$TEMPLATES_DIR/btcctl-alias.sh" >> "$alias_file"
535 | chmod +x "$alias_file"
536 |
537 | # point generic btcctl to first btcd node
538 | local default_alias_file="$ALIASES_DIR_NAME/btcctl"
539 | if [[ ! -f "$default_alias_file" ]]; then
540 | ln -s "$NAME" ${default_alias_file}
541 | fi
542 |
543 | prepare_btcd_volumes "$NAME"
544 |
545 | advance_common_counters
546 | advance_btcd_counters
547 |
548 | LAST_BTCD_SERVICE=${NAME}
549 | LAST_BITCOIN_SERVICE=${LAST_BTCD_SERVICE}
550 | }
551 |
552 | add_bitcoind() {
553 | NAME=$1
554 |
555 | # generate default name if not given
556 | if [[ -z "$NAME" ]]; then
557 | advance_bitcoind_auto_name_counter
558 | NAME=$(gen_bitcoind_auto_name)
559 | fi
560 |
561 | echo_service_separator bitcoind "${NAME}" ${BITCOIND_COUNTER}
562 | eval_template "$TEMPLATES_DIR/bitcoind.yml" >> ${COMPOSE_FILE}
563 |
564 | local alias_file="$ALIASES_DIR_NAME/$NAME"
565 | eval_script_template "$TEMPLATES_DIR/bitcoin-cli-alias.sh" >> "$alias_file"
566 | chmod +x "$alias_file"
567 |
568 | # point generic bitcoin-cli to first bitcoind node
569 | local default_alias_file="$ALIASES_DIR_NAME/bitcoin-cli"
570 | if [[ ! -f "$default_alias_file" ]]; then
571 | ln -s "$NAME" ${default_alias_file}
572 | fi
573 |
574 | prepare_bitcoind_volumes "$NAME"
575 |
576 | advance_common_counters
577 | advance_bitcoind_counters
578 |
579 | LAST_BITCOIND_SERVICE=${NAME}
580 | LAST_BITCOIN_SERVICE=${LAST_BITCOIND_SERVICE}
581 | }
582 |
583 | add_lightning() {
584 | NAME=$1
585 |
586 | # generate default name if not given
587 | if [[ -z "$NAME" ]]; then
588 | advance_lightning_auto_name_counter
589 | NAME=$(gen_lightning_auto_name)
590 | fi
591 |
592 | # auto-provide LIGHTNING_BITCOIN_RPC_HOST if not given
593 | local prev_lightning_bitcoin_rpc_host="$LIGHTNING_BITCOIN_RPC_HOST"
594 | if [[ -z "$LIGHTNING_BITCOIN_RPC_HOST" ]]; then
595 | LIGHTNING_BITCOIN_RPC_HOST="$(get_last_bitcoin_service)"
596 | fi
597 | local prev_lightning_backend="$LIGHTNING_BACKEND"
598 | if [[ -z "$LIGHTNING_BACKEND" ]]; then
599 | LIGHTNING_BACKEND="$(get_last_bitcoin_backend)"
600 | if [[ "$LIGHTNING_BACKEND" != "bitcoind" ]]; then
601 | echo_err "lightning node '$NAME' needs bitcoind backend, add at least one bitcoind before adding lightning nodes"
602 | exit ${ERR_REQUIRE_BITCOIND}
603 | fi
604 | fi
605 |
606 | echo_service_separator lightning "${NAME}" ${LIGHTNING_COUNTER}
607 | eval_template "$TEMPLATES_DIR/lightning.yml" >> ${COMPOSE_FILE}
608 |
609 | local alias_file="$ALIASES_DIR_NAME/$NAME"
610 | eval_script_template "$TEMPLATES_DIR/lightning-cli-alias.sh" >> "$alias_file"
611 | chmod +x "$alias_file"
612 |
613 | # point generic lightning-cli to the first lightning node
614 | local default_alias_file="$ALIASES_DIR_NAME/lightning-cli"
615 | if [[ ! -f "$default_alias_file" ]]; then
616 | ln -s "$NAME" ${default_alias_file}
617 | fi
618 |
619 | prepare_lightning_volumes "$NAME"
620 |
621 | advance_common_counters
622 | advance_lightning_counters
623 |
624 | LIGHTNING_BITCOIN_RPC_HOST="$prev_lightning_bitcoin_rpc_host"
625 | LIGHTNING_BACKEND="$prev_lightning_backend"
626 | }
627 |
628 | add_eclair() {
629 | NAME=$1
630 |
631 | # generate default name if not given
632 | if [[ -z "$NAME" ]]; then
633 | advance_eclair_auto_name_counter
634 | NAME=$(gen_eclair_auto_name)
635 | fi
636 |
637 | # auto-provide ECLAIR_BITCOIN_RPC_HOST if not given
638 | local prev_eclair_bitcoin_rpc_host="$ECLAIR_BITCOIN_RPC_HOST"
639 | if [[ -z "$ECLAIR_BITCOIN_RPC_HOST" ]]; then
640 | ECLAIR_BITCOIN_RPC_HOST="$(get_last_bitcoin_service)"
641 | fi
642 | local prev_eclair_backend="$ECLAIR_BACKEND"
643 | if [[ -z "$ECLAIR_BACKEND" ]]; then
644 | ECLAIR_BACKEND="$(get_last_bitcoin_backend)"
645 | if [[ "$ECLAIR_BACKEND" != "bitcoind" ]]; then
646 | echo_err "lightning node '$NAME' needs bitcoind backend, add at least one bitcoind before adding eclair nodes"
647 | exit ${ERR_REQUIRE_BITCOIND}
648 | fi
649 | fi
650 |
651 | echo_service_separator eclair "${NAME}" ${ECLAIR_COUNTER}
652 | eval_template "$TEMPLATES_DIR/eclair.yml" >> ${COMPOSE_FILE}
653 |
654 | local alias_file="$ALIASES_DIR_NAME/$NAME"
655 | eval_script_template "$TEMPLATES_DIR/eclair-cli-alias.sh" >> "$alias_file"
656 | chmod +x "$alias_file"
657 |
658 | # point generic lncli to first eclair node
659 | local default_alias_file="$ALIASES_DIR_NAME/eclair-cli"
660 | if [[ ! -f "$default_alias_file" ]]; then
661 | ln -s "$NAME" ${default_alias_file}
662 | fi
663 |
664 | prepare_eclair_volumes "$NAME"
665 |
666 | advance_common_counters
667 | advance_eclair_counters
668 |
669 | ECLAIR_BITCOIN_RPC_HOST="$prev_eclair_bitcoin_rpc_host"
670 | ECLAIR_BACKEND="$prev_eclair_backend"
671 | }
672 |
673 | # -- public API -------------------------------------------------------------------------------------------------------------
674 |
675 | prelude() {
676 | scaffold_simnet
677 | add_toolbox
678 | create_aliases_dir
679 | prepare_repos
680 | init_states
681 | prepare_pre_volumes
682 | prepare_tmux_script
683 | prepare_home_link
684 | eval_template "$TEMPLATES_DIR/prelude.yml" > ${COMPOSE_FILE}
685 | touch docker-compose.yml
686 | PRELUDE_DONE=1
687 | }
688 |
689 | # add [flavor] [name] ...
690 | add() {
691 | local kind=$1
692 | shift
693 |
694 | # add commands must be called AFTER prelude
695 | if [[ -z "${PRELUDE_DONE}" ]]; then
696 | echo_err_with_stack_trace "prelude not called prior calling first add"
697 | exit ${ERR_MUST_CALL_PRELUDE_FIRST}
698 | fi
699 |
700 | case "$kind" in
701 | "btcd") add_btcd "$@" ;;
702 | "lnd") add_lnd "$@" ;;
703 | "bitcoind") add_bitcoind "$@" ;;
704 | "lightningd"|"lightning"|"c-lightning") add_lightning "$@" ;;
705 | "eclair") add_eclair "$@" ;;
706 | *) echo "unsupported service '$kind', currently allowed are 'btcd' or 'lnd'" ;;
707 | esac
708 | }
709 |
710 | # -- initialization ---------------------------------------------------------------------------------------------------------
711 |
712 | init_defaults
713 | reset_counters
714 |
715 | cd "$SIMVERSE_WORKSPACE"
716 | if [[ ! -d "$SIMNET_NAME" ]]; then
717 | mkdir "$SIMNET_NAME"
718 | fi
719 | cd "$SIMNET_NAME"
--------------------------------------------------------------------------------