Skip to content

Commit

Permalink
Merge pull request #2641 from saurabhkumarkardam/substrate-multi-org-…
Browse files Browse the repository at this point in the history
…support

feat(substrate): multi-organization support
  • Loading branch information
sownak authored Jan 14, 2025
2 parents e130828 + dee0261 commit 24567ee
Show file tree
Hide file tree
Showing 30 changed files with 860 additions and 647 deletions.
365 changes: 313 additions & 52 deletions platforms/substrate/charts/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ apiVersion: getambassador.io/v3alpha1
kind: Listener
metadata:
name: "{{ $fullname }}-listener"
namespace: {{ $.Values.namespace }}
namespace: {{ $.Release.Namespace }}
spec:
port: {{ $.Values.proxy.port }}
protocol: TCP
Expand All @@ -59,8 +59,8 @@ apiVersion: getambassador.io/v3alpha1
kind: TCPMapping
metadata:
name: "{{ $fullname }}-tcpmapping"
namespace: {{ $.Values.namespace }}
namespace: {{ $.Release.Namespace }}
spec:
port: {{ $.Values.proxy.port }}
service: "{{ $fullname }}-swarm.{{ $.Values.namespace }}:{{ $.Values.config.ipfsSwarmPort }}"
service: "{{ $fullname }}-swarm.{{ $.Release.Namespace }}:{{ $.Values.config.ipfsSwarmPort }}"
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ spec:

peer_id=$(cat config | jq -r .Identity.PeerID)
private_key=$(cat config | jq -r .Identity.PrivKey)
echo "peer_id: ${peer_id}"

{{- if eq .Values.global.vault.type "hashicorp" }}
echo "
Expand All @@ -169,7 +170,7 @@ spec:
jq -r 'if .errors then . else .auth.client_token end')

validateVaultResponse 'vault login token' "${VAULT_CLIENT_TOKEN}"
vault_secret_key="${VAULT_SECRET_ENGINE}"/"${VAULT_SECRET_PREFIX}-ipfs-keys"
vault_secret_key="${VAULT_SECRET_ENGINE}"/"${VAULT_SECRET_PREFIX}/{{ .Release.Name }}-ipfs-keys"
# Save the generated keys to VAULT
LOOKUP_SECRET_RESPONSE=$(curl -sS -H "X-Vault-Token: ${VAULT_CLIENT_TOKEN}" \
-H "Content-Type: application/json" \
Expand Down
5 changes: 3 additions & 2 deletions platforms/substrate/charts/dscp-ipfs-node/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ config:
healthCheckPollPeriod: 30000
healthCheckTimeout: 2000
# External DSCP-Node hostname to query, this overrides dscpNode.enabled
nodeHost: member-1-substrate-node
# nodeHost: oem-member-1-node-substrate-node-0 # member-1-substrate-node
nodeHost:
# External DSCP-Node port to query
nodePort: 9944
# Public key for the IPFS subsystem
Expand All @@ -66,7 +67,7 @@ config:
ipfsLogLevel: info
# IPFS boot node addresses in MultiAddress format for the IPFS subsystem
## ref: https://multiformats.io/multiaddr/
ipfsBootNodeAddress: ""
ipfsBootNodeAddress:

service:
# dscp-ipfs swarm service configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ spec:
args:
- |
{{- if .Values.removeGenesisOnDelete.enabled }}
secret_names=$(kubectl get secret -n {{ .Release.Namespace }} | grep substrate-node | awk '{print $1}')
while IFS= read -r nodeKeys; do
kubectl delete secret "$nodeKeys" --namespace {{ .Release.Namespace }}
done <<< "$secret_names"
if kubectl get configmap "substrate-genesis" --namespace {{ .Release.Namespace }} &> /dev/null; then
kubectl delete configmap "substrate-genesis" --namespace {{ .Release.Namespace }}
kubectl delete configmap "substrate-genesis" --namespace {{ .Release.Namespace }}
fi
{{- end}}
137 changes: 80 additions & 57 deletions platforms/substrate/charts/substrate-genesis/templates/genesis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ spec:
chmod +x kubectl
mv kubectl /usr/local/bin/
kubectl version --client
echo "Step 2: Execute the provided command to generate the genesis block."
mkdir certcheck
command={{ $.Values.node.command }}
echo "Generate genesis"
GENESIS=$($command build-spec --disable-default-bootnode --chain local)
echo "Step 3: Edit genesis configuration."
# Set values
GENESIS=$(echo $GENESIS | jq '.name |= {{ .Values.chain | quote }}')
Expand All @@ -114,7 +114,7 @@ spec:
GENESIS=$(echo $GENESIS | jq '.genesis.runtime.nodeAuthorization.nodes |= []')
GENESIS=$(echo $GENESIS | jq '.genesis.runtime.membership.members |= []')
{{- end }}
echo "Step 4: Generate sudo key with Sr25519 scheme and add sudo account key and balance into genesis."
# Generate sudo key with Sr25519 scheme
$command key generate --scheme Sr25519 --output-type json >> certcheck/sudoKeygenOutput.json
Expand All @@ -123,80 +123,103 @@ spec:
# Add sudo account key and balance into genesis
GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR --arg balance 1152921504606846976 '.genesis.runtime.balances.balances += [[$sudo, ($balance | tonumber)]]')
GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR '.genesis.runtime.sudo.key |= $sudo')
echo "Step 5: Insert AURA & GRANDPA keys into genesis for validators."
echo "Inserting keys into genesis for validators"
for ((i=1; i<={{ $.Values.node.validator.count }}; i++)); do
secret_data=$(kubectl get secret "substrate-node-validator-${i}-keys" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d)
# Iterate over each organization namespace
{{- range .Values.org_namespaces }}
# Extract the org name (first key in the current item)
{{- $org_name := . | keys | first }}
# Access the 'validator' list using the index function and print each validator
{{- $validators := index . $org_name "validator" }}
# Check if secret_data is empty or not
if [ -n "$secret_data" ]; then
# Extract aura_addr and grandpa_addr
aura_addr=$(echo "$secret_data" | jq -r '.data.aura_addr')
grandpa_addr=$(echo "$secret_data" | jq -r '.data.grandpa_addr')
{{- range $validators }}
echo " Validator: {{ . }}"
secret_data=$(kubectl get secret "{{ . }}-keys" --namespace {{ $org_name }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d)
# Check if extraction successful
if [ -n "$aura_addr" ] && [ -n "$grandpa_addr" ]; then
# Insert aura_addr keys into GENESIS JSON
GENESIS=$(echo "$GENESIS" | jq --arg aura "$aura_addr" '.genesis.runtime.aura.authorities += [$aura]')
GENESIS=$(echo "$GENESIS" | jq --arg grandpa "$grandpa_addr" '.genesis.runtime.grandpa.authorities += [[$grandpa, 1]]')
# Check if secret_data is empty or not
if [ -n "$secret_data" ]; then
# Extract aura_addr and grandpa_addr
aura_addr=$(echo "$secret_data" | jq -r '.data.aura_addr')
grandpa_addr=$(echo "$secret_data" | jq -r '.data.grandpa_addr')
# Check if extraction successful
if [ -n "$aura_addr" ] && [ -n "$grandpa_addr" ]; then
# Insert aura_addr keys into GENESIS JSON
GENESIS=$(echo "$GENESIS" | jq --arg aura "$aura_addr" '.genesis.runtime.aura.authorities += [$aura]')
GENESIS=$(echo "$GENESIS" | jq --arg grandpa "$grandpa_addr" '.genesis.runtime.grandpa.authorities += [[$grandpa, 1]]')
else
echo "Error: Unable to retrieve aura_addr or grandpa_addr key for validator $validator"
fi
else
echo "Error: Unable to retrieve aura_addr or grandpa_addr key"
echo "Error: Unable to retrieve aura_addr or grandpa_addr key for validator $validator."
fi
else
echo "Error: Unable to retrieve data."
fi
done
# Initialize an array to store each nodes' Kubernetes secret name
secret_names=$(kubectl get secret -n {{ .Release.Namespace }} | grep substrate-node | awk '{print $1}')
declare -a nodes
while IFS= read -r line; do
nodes+=("$line")
done <<< "$secret_names"
{{- end }}
{{- end }}
echo "Key insertion process completed."
echo "Step 6: Adding member accounts and their balances to genesis."
for ((i=1; i<={{ $.Values.node.member.count }}; i++)); do
account_addr=$(kubectl get secret "substrate-node-member-$i-keys" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.account_addr')
# Add account address and balance into genesis block
GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" --arg balance {{ $.Values.node.member.balance }} '.genesis.runtime.balances.balances += [[$account_id, ($balance | tonumber)]]')
GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" '.genesis.runtime.membership.members += [$account_id]')
{{- range .Values.org_namespaces }}
# Extract the org name (first key in the current item)
{{- $org_name := . | keys | first }}
# Access the 'member' list
{{- $members := index . $org_name "member" }}
# Access the 'validator' list
{{- $validators := index . $org_name "validator" }}
# Loop through each node to add authorization for the current member
for node in "${nodes[@]}"; do
# Retrieve node ID for the current node
node_id=$(kubectl get secret "$node" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_id')
# Iterate through members
{{- range $members }}
account_addr=$(kubectl get secret "{{ . }}-keys" --namespace "{{ $org_name }}" -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.account_addr')
# Convert node ID to base58 format
base58=$(echo -n "$node_id" | base58 -d | xxd -p | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]')
GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" --arg balance {{ $.Values.node.member.balance }} '.genesis.runtime.balances.balances += [[$account_id, ($balance | tonumber)]]')
GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" '.genesis.runtime.membership.members += [$account_id]')
# Split the base58 string into an array of bytes
arr_node_id=($(echo $base58 | fold -w2))
# Add authorization for the current member to the node
GENESIS=$(echo $GENESIS | jq --arg owner "$account_addr" '.genesis.runtime.nodeAuthorization.nodes += [[[], $owner]]')
for byte in "${arr_node_id[@]}"
do
# Add each byte of the node ID to the authorization
GENESIS=$(echo $GENESIS | jq --arg byte $(echo "obase=10; ibase=16; $byte" | bc) '.genesis.runtime.nodeAuthorization.nodes[-1][0] += [($byte | tonumber)]')
done
done
done
{{- range $validators }}
node_id=$(kubectl get secret "{{ . }}-keys" --namespace "{{ $org_name }}" -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_id')
base58=$(echo -n "$node_id" | base58 -d | xxd -p | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]')
arr_node_id=($(echo $base58 | fold -w2))
GENESIS=$(echo $GENESIS | jq --arg owner "$account_addr" '.genesis.runtime.nodeAuthorization.nodes += [[[], $owner]]')
for byte in "${arr_node_id[@]}"
do
# Add each byte of the node ID to the authorization
GENESIS=$(echo $GENESIS | jq --arg byte $(echo "obase=10; ibase=16; $byte" | bc) '.genesis.runtime.nodeAuthorization.nodes[-1][0] += [($byte | tonumber)]')
done
{{- end }}
{{- range $members }}
node_id=$(kubectl get secret "{{ . }}-keys" --namespace "{{ $org_name }}" -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_id')
base58=$(echo -n "$node_id" | base58 -d | xxd -p | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]')
arr_node_id=($(echo $base58 | fold -w2))
GENESIS=$(echo $GENESIS | jq --arg owner "$account_addr" '.genesis.runtime.nodeAuthorization.nodes += [[[], $owner]]')
for byte in "${arr_node_id[@]}"
do
# Add each byte of the node ID to the authorization
GENESIS=$(echo $GENESIS | jq --arg byte $(echo "obase=10; ibase=16; $byte" | bc) '.genesis.runtime.nodeAuthorization.nodes[-1][0] += [($byte | tonumber)]')
done
{{- end }}
{{- end }}
{{- end }}
echo "Step 7: Update the format of the modified genesis JSON and create a config map if it doesn't exist."
# Write the modified genesis JSON to a file
echo "$GENESIS" > certcheck/genesis.json
# Convert the genesis JSON to raw format
echo "Converting genesis to raw format"
GENESIS=$($command build-spec --disable-default-bootnode --raw --chain certcheck/genesis.json)
echo "$GENESIS" > certcheck/genesis_raw.json
# Encode the raw genesis JSON to base64
cat certcheck/genesis_raw.json | base64 -w0 > certcheck/genesis_base64 # No need to encode it if you wanna store genesis as a K8s secret
# # Create the config map "substrate-genesis" using the base64 encoded genesis JSON and sudoKeygenOutput.json if it doesn't exist
if ! kubectl get configmap "substrate-genesis" --namespace {{ .Release.Namespace }} &> /dev/null; then
kubectl create configmap "substrate-genesis" --namespace {{ .Release.Namespace }} --from-file=genesis="${MOUNT_PATH}/genesis_base64" --from-file=sudoKeygenOutput="${MOUNT_PATH}/sudoKeygenOutput.json"
fi
{{- range .Values.org_namespaces }}
# Extract the org name (first key in the current item)
{{- $org_name := . | keys | first }}
# Create the config map "substrate-genesis" using the base64 encoded genesis JSON and sudoKeygenOutput.json if it doesn't exist
if ! kubectl get configmap "substrate-genesis" --namespace {{ $org_name }} &> /dev/null; then
kubectl create configmap "substrate-genesis" --namespace {{ $org_name }} --from-file=genesis="${MOUNT_PATH}/genesis_base64" --from-file=sudoKeygenOutput="${MOUNT_PATH}/sudoKeygenOutput.json"
fi
{{- end }}
echo "COMPLETED!"
{{- if eq .Values.global.vault.type "hashicorp" }}
# Initialize the token
Expand Down
33 changes: 24 additions & 9 deletions platforms/substrate/charts/substrate-genesis/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,28 @@
# The following are for overriding global values
global:
# Provide the service account name which will be created.
serviceAccountName: vault-auth
serviceAccountName: global-vault-sa
cluster:
provider: azure # choose from: minikube | aws
provider: # choose from: minikube | aws | azure
cloudNativeServices: false # 'false' is implemented
#Provide the kubernetes host url
#Eg. kubernetesUrl: https://10.3.8.5:6443
kubernetesUrl:
vault:
# Provide the type of vault
type: kubernetes # hashicorp | kubernetes
type: hashicorp # hashicorp | kubernetes
# Provide the vault role used.
role: vault-role
role: # global-vault-role
# Provide the network type
network: substrate
# Provide the vault server address
address: # "http://vault_url"
# Provide the vault authPath configured to be used.
authPath: supplychain
authPath: oem
# Provide the secret engine.
secretEngine: secretsv2
# Provide the vault path where the secrets will be stored
secretPrefix: "data/supplychain"
secretPrefix: "data/oem"

removeGenesisOnDelete:
enabled: true
Expand All @@ -59,8 +59,23 @@ node:
# Command to be invoked to perform operations on the node
# Eg. command: substrate
command: ./dscp-node
validator:
count: 4
member:
count: 1
balance: 1152921504606846976

org_namespaces:
- oem-subs:
validator:
- oem-validator-1
- oem-validator-2
member:
- oem-member-1
- tierone-subs:
validator:
- tierone-validator-3
- tierone-validator-4
member:
- tierone-member-2
- tiertwo-subs:
validator:
member:
- tiertwo-member-3
9 changes: 9 additions & 0 deletions platforms/substrate/charts/substrate-key-gen/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dependencies:
- name: bevel-vault-mgmt
repository: file://../../../shared/charts/bevel-vault-mgmt
version: 1.0.1
- name: bevel-scripts
repository: file://../../../shared/charts/bevel-scripts
version: 1.0.0
digest: sha256:24f4543fa153ec12bce571445cb3cd5bf59e44e2f1177fb28299fa48865c5644
generated: "2024-12-10T09:26:12.553233538Z"
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,26 @@
#
# SPDX-License-Identifier: Apache-2.0
##############################################################################################

---
apiVersion: v2
name: substrate-key-mgmt
appVersion: 'latest'
name: substrate-key-gen
description: A Helm chart to generate the keys for Substrate Nodes
version: 1.1.0
type: application
annotations:
hyperledger-bevel/platform: substrate
licenses: Apache-2.0
home: https://github.com/hyperledger/bevel
version: 1.1.0
appVersion: latest
keywords:
- DSCP
- BEVEL
- SUBSTRATE
- bevel
- ethereum
- substrate
- hyperledger
- enterprise
- blockchain
- deployment
- accenture
home: https://hyperledger-bevel.readthedocs.io/en/latest/
sources:
- https://github.com/hyperledger/bevel
maintainers:
- name: Hyperledger Bevel maintainers
email: [email protected]
Loading

0 comments on commit 24567ee

Please sign in to comment.