From 77b724b39f4aec65e26cc18f6f27f0be316eaef9 Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Fri, 15 Jan 2021 14:06:30 -0800 Subject: [PATCH 1/7] support for multiple bakers Previously we used to create 4 accounts always. We simplify by creating just one account per baker. As a consequence, all blocks are baked on priority zero from the beginning. We add a new parameter to mkchain `--number-of-bakers`, defaults to 1. If we pass a higher number n, it will create n bakers and n nodes baking for each baker. Bakers and nodes can be mapped using values.yaml: specify which node bakes for which address. This requires jq which requires a custom baking container. This also allowed me to remove helm hacks. We make a few assumptions for now: * every baking node is also a bootstrap node * the first baker's address is also the chain activator I tested in non-zerotier mode and zerotier mode with one create namespace and one invite namespace in the same minikube cluster; it works, but required me to remove the hard-coded nodePort for p2p. --- baker/Dockerfile | 5 +++ baker/entrypoint.sh | 8 ++++ chain-initiator/entrypoint.sh | 3 +- charts/tezos/templates/bootstrap.yaml | 65 +++++++++++++++------------ charts/tezos/templates/configs.yaml | 61 +++---------------------- charts/tezos/templates/static.yaml | 17 +------ charts/tezos/values.yaml | 1 + config-generator/entrypoint.py | 4 +- devspace.yaml | 4 ++ key-importer/Dockerfile | 1 + key-importer/entrypoint.sh | 9 ++-- mkchain/tqchain/mkchain.py | 40 +++++++++++------ 12 files changed, 99 insertions(+), 119 deletions(-) create mode 100644 baker/Dockerfile create mode 100755 baker/entrypoint.sh diff --git a/baker/Dockerfile b/baker/Dockerfile new file mode 100644 index 000000000..38325f207 --- /dev/null +++ b/baker/Dockerfile @@ -0,0 +1,5 @@ +FROM tezos/tezos:v8-release +RUN sudo apk add jq bash +COPY entrypoint.sh / +ENTRYPOINT ["/entrypoint.sh"] +CMD [] diff --git a/baker/entrypoint.sh b/baker/entrypoint.sh new file mode 100755 index 000000000..b7d873ac5 --- /dev/null +++ b/baker/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -x + +baker_command=$(echo $CHAIN_PARAMS | jq -r '.baker_command') +POD_INDEX=$(echo $POD_NAME | sed -e s/tezos-bootstrap-node-//) +baker_account=$(echo $NODES | jq -r "[.[] | select(.bake_for)] | . [${POD_INDEX}].bake_for") +/usr/local/bin/${baker_command} -d /var/tezos/client run with local node /var/tezos/node ${baker_account:?Error: baker account not set} diff --git a/chain-initiator/entrypoint.sh b/chain-initiator/entrypoint.sh index d49ea2b38..c14ee8a0a 100755 --- a/chain-initiator/entrypoint.sh +++ b/chain-initiator/entrypoint.sh @@ -7,4 +7,5 @@ until nslookup tezos-bootstrap-node-rpc; do echo waiting for tezos-bootstrap-nod until wget -O- http://tezos-bootstrap-node-rpc:8732/version; do sleep 2; done; protocol_hash=$(echo $CHAIN_PARAMS | jq -r '.protocol_hash') -/usr/local/bin/tezos-client -A tezos-bootstrap-node-rpc -P 8732 -d /var/tezos/client -l --block genesis activate protocol "${protocol_hash}" with fitness -1 and key genesis and parameters /etc/tezos/parameters.json +activation_account=$(echo $CHAIN_PARAMS | jq -r '.activation_account') +/usr/local/bin/tezos-client -A tezos-bootstrap-node-rpc -P 8732 -d /var/tezos/client -l --block genesis activate protocol "${protocol_hash}" with fitness -1 and key "${activation_account}" and parameters /etc/tezos/parameters.json diff --git a/charts/tezos/templates/bootstrap.yaml b/charts/tezos/templates/bootstrap.yaml index f60d4bf3b..19d479dbf 100644 --- a/charts/tezos/templates/bootstrap.yaml +++ b/charts/tezos/templates/bootstrap.yaml @@ -1,3 +1,11 @@ +{{- /* Function to get number of bakers */}} +{{- define "bakers" }} + {{- range $i, $node := $.Values.nodes }} + {{- if (hasKey $node "bake_for") }} +{{ $i }}: true + {{- end }} + {{- end }} +{{- end -}} {{- if not .Values.is_invitation }} apiVersion: batch/v1 kind: Job @@ -61,16 +69,16 @@ spec: --- {{- if not .Values.is_invitation }} apiVersion: apps/v1 -kind: Deployment +kind: StatefulSet metadata: name: tezos-bootstrap-node namespace: {{ .Release.Namespace }} spec: + replicas: {{ include "bakers" . | fromYaml | values | len }} + serviceName: tezos-bootstrap-node-rpc selector: matchLabels: app: tezos-bootstrap-node - strategy: - type: Recreate template: metadata: labels: @@ -109,37 +117,26 @@ spec: name: config-volume - mountPath: /var/tezos name: var-volume - - args: - - "-A" - - localhost - - "-P" - - '8732' - - "-d" - - /var/tezos/client - - run - - with - - local - - node - - /var/tezos/node - - baker - command: - - "{{ .Values.baker_command }}" - image: "{{ .Values.images.tezos }}" - name: baker-job + - image: "{{ .Values.images.baker }}" imagePullPolicy: IfNotPresent + name: baker volumeMounts: - mountPath: /var/tezos name: var-volume + envFrom: + - configMapRef: + name: tezos-config + - secretRef: + name: tezos-secret + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name {{- if .Values.zerotier_in_use }} - args: - "-c" - "echo 'starting zerotier' && zerotier-one /var/tezos/zerotier" - - "-P" - - '8732' - - "-d" - - /var/tezos/client - - run - - baker command: - sh image: "{{ .Values.images.zerotier }}" @@ -226,7 +223,17 @@ spec: name: dev-net-tun - emptyDir: {} name: config-volume - - name: var-volume - persistentVolumeClaim: - claimName: tezos-bootstrap-node-pv-claim + - configMap: + name: tqtezos-utils + name: tqtezos-utils + volumeClaimTemplates: + - metadata: + name: var-volume + namespace: {{ .Release.Namespace }} + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "15Gi" {{- end }} diff --git a/charts/tezos/templates/configs.yaml b/charts/tezos/templates/configs.yaml index bc55c6d7f..d4a0141e4 100644 --- a/charts/tezos/templates/configs.yaml +++ b/charts/tezos/templates/configs.yaml @@ -6,10 +6,14 @@ data: "bootstrap_peers": {{ toJson .Values.bootstrap_peers }}, "chain_name": "{{ .Values.chain_name }}", "genesis_block": "{{ .Values.genesis.genesis_chain_id }}", + "activation_account": "{{ (index .Values.accounts 0).name }}", "timestamp": "{{ .Values.genesis.bootstrap_timestamp }}", "zerotier_in_use": {{ .Values.zerotier_in_use }}, + "baker_command": "{{ .Values.baker_command }}", "protocol_hash": "{{ .Values.protocol.hash }}" } + NODES: | +{{ .Values.nodes | toJson | indent 4}} kind: ConfigMap metadata: name: tezos-config @@ -25,63 +29,10 @@ metadata: name: zerotier-config namespace: {{ .Release.Namespace }} --- -{{- /* - -https://stackoverflow.com/questions/61153730/creating-a-filtered-list-using-helm-template-helpers - -Helm templating is very string based and so is unable to do common operations -like mapping over a list - -The BOOTSTRAP_ACCOUNTS secrets variable is essentially the following: - -```python3 -" ".join(account.name for account in Values.accounts if account.bootstrap) -``` - -So to recreate in helm, we do the following: - -1. First, use helms text templating to build a yaml list (see `define "boostrapAccounts"`) -2. Then, use the builtin `fromYaml` function to get the list of account names -3. Use the `join` builtin function - -Theres one caveat in that `fromYaml` expects an object, so we cant directly -make a yaml list. So we simply make dummmy keys and then call `values` to get -the list of values, which is the list we want - -*/ -}} - -{{- define "bootstrapAccounts" }} -value: - {{- range $i, $account := $.Values.accounts }} - {{- if $account.bootstrap }} -{{ $i }}: {{ $account.name | quote }} - {{- end }} - {{- end }} -{{- end -}} - apiVersion: v1 data: - BOOTSTRAP_ACCOUNTS: | -{{ include "bootstrapAccounts" . | fromYaml | values | join " " | b64enc | indent 4 }} -{{- if .Values.is_invitation }} - KEYS_TYPE: | -{{ "public" | b64enc | indent 4 }} - {{- range $account := $.Values.accounts }} - {{- if not $account.private }} -{{ $account.name | indent 2 }}_public_key: | -{{ $account.key | b64enc | indent 4 }} - {{- end }} - {{- end }} -{{- else }} - KEYS_TYPE: | -{{ "secret" | b64enc | indent 4 }} - {{- range $account := $.Values.accounts }} - {{- if $account.private }} -{{ $account.name | indent 2 }}_secret_key: | -{{ $account.key | b64enc | indent 4 }} - {{- end }} - {{- end }} -{{- end }} + ACCOUNTS: | +{{ .Values.accounts | toJson | b64enc | indent 4 }} kind: Secret metadata: name: tezos-secret diff --git a/charts/tezos/templates/static.yaml b/charts/tezos/templates/static.yaml index 6d0850ee5..b19860c18 100644 --- a/charts/tezos/templates/static.yaml +++ b/charts/tezos/templates/static.yaml @@ -5,8 +5,7 @@ metadata: namespace: {{ .Release.Namespace }} spec: ports: - - nodePort: 31732 - port: 8732 + - port: 8732 selector: app: tezos-bootstrap-node type: NodePort @@ -19,21 +18,9 @@ metadata: namespace: {{ .Release.Namespace }} spec: ports: - - nodePort: 30732 - port: 9732 + - port: 9732 selector: app: tezos-bootstrap-node type: NodePort --- {{ end }} -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: tezos-bootstrap-node-pv-claim - namespace: {{ .Release.Namespace }} -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: "15Gi" diff --git a/charts/tezos/values.yaml b/charts/tezos/values.yaml index 0cbb1f11a..48f5063de 100644 --- a/charts/tezos/values.yaml +++ b/charts/tezos/values.yaml @@ -11,6 +11,7 @@ images: zerotier: tezos-k8s-zerotier:dev tezos: tezos/tezos:v8-release chain_initiator: tezos-k8s-chain-initiator:dev + baker: tezos-k8s-baker:dev key_importer: tezos-k8s-key-importer:dev config_generator: tezos-k8s-config-generator:dev protocol: diff --git a/config-generator/entrypoint.py b/config-generator/entrypoint.py index fb71bc631..ed3017f07 100644 --- a/config-generator/entrypoint.py +++ b/config-generator/entrypoint.py @@ -39,7 +39,7 @@ def main(): if CHAIN_PARAMS["zerotier_in_use"]: with open("/var/tezos/zerotier_data.json", "r") as f: net_addr = json.load(f)[0]["assignedAddresses"][0].split("/")[0] - if bootstrap_peers == [] and "bootstrap" not in socket.gethostname(): + if bootstrap_peers == []: bootstrap_peers.extend(get_zerotier_bootstrap_peer_ips()) else: bootstrap_peers.append("tezos-bootstrap-node-p2p:9732") @@ -47,7 +47,7 @@ def main(): config_json = json.dumps( get_node_config( CHAIN_PARAMS["chain_name"], - bootstrap_accounts["genesis"], + bootstrap_accounts[CHAIN_PARAMS["activation_account"]], CHAIN_PARAMS["timestamp"], bootstrap_peers, CHAIN_PARAMS["genesis_block"], diff --git a/devspace.yaml b/devspace.yaml index 508e56afb..6c64e3e41 100755 --- a/devspace.yaml +++ b/devspace.yaml @@ -16,6 +16,10 @@ images: image: tezos-k8s-zerotier dockerfile: ./zerotier/Dockerfile context: ./zerotier + baker: + image: tezos-k8s-baker + dockerfile: ./baker/Dockerfile + context: ./baker chain-initiator: image: tezos-k8s-chain-initiator dockerfile: ./chain-initiator/Dockerfile diff --git a/key-importer/Dockerfile b/key-importer/Dockerfile index 2172387ba..e1cd2cf94 100644 --- a/key-importer/Dockerfile +++ b/key-importer/Dockerfile @@ -1,4 +1,5 @@ FROM tezos/tezos:v8-release +RUN sudo apk add --no-cache jq COPY entrypoint.sh / ENTRYPOINT ["/entrypoint.sh"] CMD [] diff --git a/key-importer/entrypoint.sh b/key-importer/entrypoint.sh index aa529fa0b..11b44acc7 100755 --- a/key-importer/entrypoint.sh +++ b/key-importer/entrypoint.sh @@ -2,7 +2,10 @@ mkdir -p /var/tezos/client chmod -R 777 /var/tezos/client -for acct in ${BOOTSTRAP_ACCOUNTS}; do - key=$(eval echo \$${acct}_${KEYS_TYPE}_key) - tezos-client -d /var/tezos/client --protocol PsDELPH1Kxsx import ${KEYS_TYPE} key $acct unencrypted:${key} -f +echo $ACCOUNTS | jq -c --raw-output .[] | while read line; do + key=$(echo $line | jq -r '.key') + name=$(echo $line | jq -r '.name') + keytype=$(echo $line | jq -r '.type') + printf "\nImporting key ${name}\n" + tezos-client -d /var/tezos/client --protocol PsDELPH1Kxsx import ${keytype} key ${name} unencrypted:${key} -f done diff --git a/mkchain/tqchain/mkchain.py b/mkchain/tqchain/mkchain.py index 227744c04..be9d8f9ab 100644 --- a/mkchain/tqchain/mkchain.py +++ b/mkchain/tqchain/mkchain.py @@ -35,7 +35,7 @@ def gen_key(image): "'/usr/local/bin/tezos-client --protocol PsDELPH1Kxsx gen keys mykey && /usr/local/bin/tezos-client --protocol PsDELPH1Kxsx show address mykey -S'", ).split(b"\n") - return {"public_key": extract_key(keys, 1), "secret_key": extract_key(keys, 2)} + return {"public": extract_key(keys, 1), "secret": extract_key(keys, 2)} def get_genesis_vanity_chain_id(seed_len=16): @@ -67,6 +67,11 @@ def get_genesis_vanity_chain_id(seed_len=16): "default": 1, "type": int, }, + "number_of_bakers": { + "help": "number of bakers in the cluster", + "default": 1, + "type": int, + }, "zerotier_network": {"help": "Zerotier network id for external chain access"}, "zerotier_token": {"help": "Zerotier token for external chain access"}, "bootstrap_peer": {"help": "peer ip to join"}, @@ -109,12 +114,13 @@ def main(): ) exit(1) - bootstrap_accounts = [ - "baker", - "bootstrap_account_1", - "bootstrap_account_2", - "genesis", - ] + if args.number_of_bakers < 1: + print( + f"Invalid argument --number-of-bakers {args.number_of_bakers}, must be 1 or more" + ) + exit(1) + + bootstrap_accounts = [f"baker{n}" for n in range(args.number_of_bakers)] base_constants = { "chain_name": args.chain_name, @@ -133,10 +139,9 @@ def main(): "zerotier_network": args.zerotier_network, "zerotier_token": args.zerotier_token, }, - "nodes": [{"bake_for": "baker"}] + [{}] * (args.number_of_nodes - 1), } - accounts = {"secret_key": [], "public_key": []} + accounts = {"secret": [], "public": []} for account in bootstrap_accounts: keys = gen_key(args.docker_image) for key_type in keys: @@ -144,25 +149,32 @@ def main(): { "name": account, "key": keys[key_type], - "private": key_type == "secret_key", - "bootstrap": True, - "baker": True, + "type": key_type, } ) + creation_nodes = [ + {"bake_for": f"baker{n}"} for n in range(args.number_of_bakers) + ] + [{} for n in range(args.number_of_nodes - args.number_of_bakers)] + + # for invitation, do not include baking nodes, and include at least one node + invitation_nodes = [{}] * args.number_of_nodes + bootstrap_peers = [args.bootstrap_peer] if args.bootstrap_peer else [] creation_constants = { **base_constants, - "accounts": accounts["secret_key"], + "accounts": accounts["secret"], "is_invitation": False, "bootstrap_peers": bootstrap_peers, + "nodes": creation_nodes, } invitation_constants = { **base_constants, - "accounts": accounts["public_key"], + "accounts": accounts["public"], "is_invitation": True, "bootstrap_peers": bootstrap_peers, + "nodes": invitation_nodes, } invitation_constants.pop("rpc_auth") From 0a49f46c7cdd5156753a567f01d12481e56a99b7 Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Wed, 20 Jan 2021 18:25:37 -0800 Subject: [PATCH 2/7] remove unnecessary configmap --- charts/tezos/templates/bootstrap.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/charts/tezos/templates/bootstrap.yaml b/charts/tezos/templates/bootstrap.yaml index 8dab4b684..647430c4f 100644 --- a/charts/tezos/templates/bootstrap.yaml +++ b/charts/tezos/templates/bootstrap.yaml @@ -223,9 +223,6 @@ spec: name: dev-net-tun - emptyDir: {} name: config-volume - - configMap: - name: tqtezos-utils - name: tqtezos-utils volumeClaimTemplates: - metadata: name: var-volume From aae9b768e1453f485f88b57c7ec3b1a63cbd7a86 Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Wed, 20 Jan 2021 18:43:21 -0800 Subject: [PATCH 3/7] key-import protocol from helm values --- charts/tezos/templates/bootstrap.yaml | 4 ++++ charts/tezos/templates/node.yaml | 2 ++ key-importer/entrypoint.sh | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/charts/tezos/templates/bootstrap.yaml b/charts/tezos/templates/bootstrap.yaml index 647430c4f..8e3a5750a 100644 --- a/charts/tezos/templates/bootstrap.yaml +++ b/charts/tezos/templates/bootstrap.yaml @@ -32,6 +32,8 @@ spec: envFrom: - secretRef: name: tezos-secret + - configMapRef: + name: tezos-config volumeMounts: - mountPath: /var/tezos name: var-volume @@ -180,6 +182,8 @@ spec: envFrom: - secretRef: name: tezos-secret + - configMapRef: + name: tezos-config volumeMounts: - mountPath: /var/tezos name: var-volume diff --git a/charts/tezos/templates/node.yaml b/charts/tezos/templates/node.yaml index d814a38e7..42d8fa6fe 100644 --- a/charts/tezos/templates/node.yaml +++ b/charts/tezos/templates/node.yaml @@ -107,6 +107,8 @@ spec: envFrom: - secretRef: name: tezos-secret + - configMapRef: + name: tezos-config volumeMounts: - mountPath: /var/tezos name: var-volume diff --git a/key-importer/entrypoint.sh b/key-importer/entrypoint.sh index 11b44acc7..2f787f291 100755 --- a/key-importer/entrypoint.sh +++ b/key-importer/entrypoint.sh @@ -6,6 +6,7 @@ echo $ACCOUNTS | jq -c --raw-output .[] | while read line; do key=$(echo $line | jq -r '.key') name=$(echo $line | jq -r '.name') keytype=$(echo $line | jq -r '.type') + protocol=$(echo $CHAIN_PARAMS | jq -r '.protocol_hash') printf "\nImporting key ${name}\n" - tezos-client -d /var/tezos/client --protocol PsDELPH1Kxsx import ${keytype} key ${name} unencrypted:${key} -f + tezos-client -d /var/tezos/client --protocol ${protocol} import ${keytype} key ${name} unencrypted:${key} -f done From fafc255410fda2000f3f2e15ce1dcaa0d86b48ab Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Wed, 20 Jan 2021 18:48:04 -0800 Subject: [PATCH 4/7] fix #69 invite yaml should contain only one node --- mkchain/tqchain/mkchain.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkchain/tqchain/mkchain.py b/mkchain/tqchain/mkchain.py index be9d8f9ab..b4b396c0b 100644 --- a/mkchain/tqchain/mkchain.py +++ b/mkchain/tqchain/mkchain.py @@ -157,8 +157,7 @@ def main(): {"bake_for": f"baker{n}"} for n in range(args.number_of_bakers) ] + [{} for n in range(args.number_of_nodes - args.number_of_bakers)] - # for invitation, do not include baking nodes, and include at least one node - invitation_nodes = [{}] * args.number_of_nodes + invitation_nodes = [{}] bootstrap_peers = [args.bootstrap_peer] if args.bootstrap_peer else [] From c3c5097642807989869355df76ae1f8f3359c292 Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Thu, 21 Jan 2021 09:46:17 -0800 Subject: [PATCH 5/7] spin up bakers in parallel --- charts/tezos/templates/bootstrap.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/tezos/templates/bootstrap.yaml b/charts/tezos/templates/bootstrap.yaml index 8e3a5750a..1100966a4 100644 --- a/charts/tezos/templates/bootstrap.yaml +++ b/charts/tezos/templates/bootstrap.yaml @@ -76,6 +76,7 @@ metadata: name: tezos-bootstrap-node namespace: {{ .Release.Namespace }} spec: + podManagementPolicy: Parallel replicas: {{ include "bakers" . | fromYaml | values | len }} serviceName: tezos-bootstrap-node-rpc selector: From a1bef7a1ff356d3c44901556e7c9dd8ae651146e Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Thu, 21 Jan 2021 10:05:47 -0800 Subject: [PATCH 6/7] add doc for `--number-of-bakers` --- mkchain/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkchain/README.md b/mkchain/README.md index ad4859dd4..dd10e1ed5 100644 --- a/mkchain/README.md +++ b/mkchain/README.md @@ -66,7 +66,8 @@ You can explicitly specify some values by: | -------------------------------- | ------------------ | --------------------------------------------------------------------------- | ---------------------- | | bootstrap_peer | --bootstrap-peer | Peer ip to connect to | | | images.tezos | --docker-image | Version of the Tezos docker image | tezos/tezos:v8-release | -| number_of_nodes | --number-of-nodes | Number of peers in the cluster | 1 | +| number_of_nodes | --number-of-nodes | Number of peers in the cluster (including bakers) | 1 | +| number_of_bakers | --number-of-bakers | Number of bakers in the cluster | 1 | | rpc_auth | --rpc-auth | Whether or not an [RPC auth](../rpc-auth/README.md) backend will be spun up | False | | zerotier_config.zerotier_network | --zerotier-network | Zerotier network id for external chain access | | | zerotier_config.zerotier_token | --zerotier-token | Zerotier token for external chain access | | From 0a952371c36d38a1f03d12e9a08f32ffcdf6c9c3 Mon Sep 17 00:00:00 2001 From: Nicolas Ochem Date: Thu, 21 Jan 2021 10:16:42 -0800 Subject: [PATCH 7/7] fix doc per review --- mkchain/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkchain/README.md b/mkchain/README.md index dd10e1ed5..a72a4f515 100644 --- a/mkchain/README.md +++ b/mkchain/README.md @@ -66,8 +66,8 @@ You can explicitly specify some values by: | -------------------------------- | ------------------ | --------------------------------------------------------------------------- | ---------------------- | | bootstrap_peer | --bootstrap-peer | Peer ip to connect to | | | images.tezos | --docker-image | Version of the Tezos docker image | tezos/tezos:v8-release | -| number_of_nodes | --number-of-nodes | Number of peers in the cluster (including bakers) | 1 | -| number_of_bakers | --number-of-bakers | Number of bakers in the cluster | 1 | +| number_of_nodes | --number-of-nodes | Total number of nodes in the cluster | 1 | +| number_of_bakers | --number-of-bakers | Number of nodes in the cluster that are bakers | 1 | | rpc_auth | --rpc-auth | Whether or not an [RPC auth](../rpc-auth/README.md) backend will be spun up | False | | zerotier_config.zerotier_network | --zerotier-network | Zerotier network id for external chain access | | | zerotier_config.zerotier_token | --zerotier-token | Zerotier token for external chain access | |