From 50cc1bc779759c3f3f6d7fc7483c16c4c53b851f Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 12:46:42 +0530 Subject: [PATCH 01/90] feat: reef chain bootstrap --- blueprints/reef/docker-compose.yml | 22 ++++++++++ blueprints/reef/reef.svg | 68 ++++++++++++++++++++++++++++++ blueprints/reef/template.toml | 28 ++++++++++++ meta.json | 17 ++++++++ 4 files changed, 135 insertions(+) create mode 100644 blueprints/reef/docker-compose.yml create mode 100644 blueprints/reef/reef.svg create mode 100644 blueprints/reef/template.toml diff --git a/blueprints/reef/docker-compose.yml b/blueprints/reef/docker-compose.yml new file mode 100644 index 000000000..45426bfb4 --- /dev/null +++ b/blueprints/reef/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3.9" + +services: + reef-bootnode: + image: anukulpandey/reef-node:latest + platform: linux/amd64 + restart: unless-stopped + environment: + - P2P_PORT=${P2P_PORT} + - RPC_PORT=${RPC_PORT} + - NODE_NAME=${NODE_NAME} + - CHAIN_SPEC_URL=${CHAIN_SPEC_URL} + volumes: + - ./bootnode-db:/reef-data + entrypoint: sh -c + command: > + "wget -qO /reef-data/customSpec.json $CHAIN_SPEC_URL && + reef-node build-spec --disable-default-bootnode --chain /reef-data/customSpec.json --raw > /reef-data/customSpecRaw.json && + reef-node --base-path /reef-data --chain /reef-data/customSpecRaw.json --port $P2P_PORT --rpc-port $RPC_PORT --no-telemetry --name $NODE_NAME --rpc-cors all --rpc-external" + ports: + - "${P2P_PORT}:${P2P_PORT}" + - "${RPC_PORT}:${RPC_PORT}" diff --git a/blueprints/reef/reef.svg b/blueprints/reef/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef/template.toml b/blueprints/reef/template.toml new file mode 100644 index 000000000..8a719955e --- /dev/null +++ b/blueprints/reef/template.toml @@ -0,0 +1,28 @@ +[metadata] +name = "reef-bootnode" +version = "1.0" +description = "Reef Bootnode template for Dokploy" + +[variables] +NODE_NAME = { description = "Node name", default = "bootnode" } +P2P_PORT = { description = "P2P port for the node", default = "30333" } +RPC_PORT = { description = "RPC/WebSocket port for the node", default = "9944" } +CHAIN_SPEC_URL = { description = "URL to fetch the custom chain spec", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } + +[config] +[[config.domains]] +serviceName = "reef-bootnode" +port = "${RPC_PORT}" +host = "localhost" +path = "/" + +env = [ + "NODE_NAME=${NODE_NAME}", + "P2P_PORT=${P2P_PORT}", + "RPC_PORT=${RPC_PORT}", + "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}" +] + +[[config.mounts]] +filePath = "/reef-data" +content = "" diff --git a/meta.json b/meta.json index 9dab7f278..aa8671333 100644 --- a/meta.json +++ b/meta.json @@ -4294,6 +4294,23 @@ "rabbitmq" ] }, + { + "id": "reef", + "name": "Reef Bootnode Validator", + "version": "1", + "description": "Runs bootstrap validator for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "validator", + "self-hosted", + "evm" + ] + }, { "id": "registry", "name": "Docker Registry", From f5949cb2048d5f55ecde51535bd1d59b8a5a2c9c Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 12:49:47 +0530 Subject: [PATCH 02/90] fix: missing doc field --- meta.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meta.json b/meta.json index aa8671333..5f2092ecf 100644 --- a/meta.json +++ b/meta.json @@ -4301,7 +4301,8 @@ "description": "Runs bootstrap validator for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node" + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs":"https://docs.reef.io/" }, "logo": "reef.svg", "tags": [ From 91653cddb3a068dab6a97a583c42d178bc020d59 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 12:55:20 +0530 Subject: [PATCH 03/90] feat: reef rpc --- .../docker-compose.yml | 0 blueprints/{reef => reef-bootnode}/reef.svg | 0 .../{reef => reef-bootnode}/template.toml | 0 blueprints/reef-rpc/docker-compose.yml | 23 +++++++ blueprints/reef-rpc/reef.svg | 68 +++++++++++++++++++ blueprints/reef-rpc/template.toml | 30 ++++++++ meta.json | 24 ++++++- 7 files changed, 142 insertions(+), 3 deletions(-) rename blueprints/{reef => reef-bootnode}/docker-compose.yml (100%) rename blueprints/{reef => reef-bootnode}/reef.svg (100%) rename blueprints/{reef => reef-bootnode}/template.toml (100%) create mode 100644 blueprints/reef-rpc/docker-compose.yml create mode 100644 blueprints/reef-rpc/reef.svg create mode 100644 blueprints/reef-rpc/template.toml diff --git a/blueprints/reef/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml similarity index 100% rename from blueprints/reef/docker-compose.yml rename to blueprints/reef-bootnode/docker-compose.yml diff --git a/blueprints/reef/reef.svg b/blueprints/reef-bootnode/reef.svg similarity index 100% rename from blueprints/reef/reef.svg rename to blueprints/reef-bootnode/reef.svg diff --git a/blueprints/reef/template.toml b/blueprints/reef-bootnode/template.toml similarity index 100% rename from blueprints/reef/template.toml rename to blueprints/reef-bootnode/template.toml diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml new file mode 100644 index 000000000..59f0a8380 --- /dev/null +++ b/blueprints/reef-rpc/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.9" + +services: + reef-rpc: + image: anukulpandey/reef-node:latest + platform: linux/amd64 + restart: unless-stopped + environment: + - P2P_PORT=${P2P_PORT} + - RPC_PORT=${RPC_PORT} + - NODE_NAME=${NODE_NAME} + - CHAIN_SPEC_URL=${CHAIN_SPEC_URL} + - BOOTNODE_ADDR=${BOOTNODE_ADDR} + volumes: + - ./rpc-db:/reef-data + entrypoint: sh -c + command: > + "wget -qO /reef-data/customSpec.json $CHAIN_SPEC_URL && + reef-node build-spec --disable-default-bootnode --chain /reef-data/customSpec.json --raw > /reef-data/customSpecRaw.json && + reef-node --base-path /reef-data --chain /reef-data/customSpecRaw.json --port $P2P_PORT --rpc-port $RPC_PORT --no-telemetry --name $NODE_NAME --rpc-cors all --rpc-external --rpc-methods Unsafe --rpc-max-connections 10000 --pruning=archive --bootnodes $BOOTNODE_ADDR" + ports: + - "${P2P_PORT}:${P2P_PORT}" + - "${RPC_PORT}:${RPC_PORT}" diff --git a/blueprints/reef-rpc/reef.svg b/blueprints/reef-rpc/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-rpc/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml new file mode 100644 index 000000000..422b1407c --- /dev/null +++ b/blueprints/reef-rpc/template.toml @@ -0,0 +1,30 @@ +[metadata] +name = "reef-rpc" +version = "1.0" +description = "Reef RPC Node template for Dokploy" + +[variables] +NODE_NAME = { description = "RPC node name", default = "rpc-node" } +P2P_PORT = { description = "P2P port for the RPC node", default = "30334" } +RPC_PORT = { description = "RPC/WebSocket port for the RPC node", default = "9945" } +CHAIN_SPEC_URL = { description = "URL to fetch the custom chain spec", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } +BOOTNODE_ADDR = { description = "Bootnode multiaddr to connect with", default = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" } + +[config] +[[config.domains]] +serviceName = "reef-rpc" +port = "${RPC_PORT}" +host = "localhost" +path = "/" + +env = [ + "NODE_NAME=${NODE_NAME}", + "P2P_PORT=${P2P_PORT}", + "RPC_PORT=${RPC_PORT}", + "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}", + "BOOTNODE_ADDR=${BOOTNODE_ADDR}" +] + +[[config.mounts]] +filePath = "/reef-data" +content = "" diff --git a/meta.json b/meta.json index 5f2092ecf..cbc54386c 100644 --- a/meta.json +++ b/meta.json @@ -4295,10 +4295,10 @@ ] }, { - "id": "reef", - "name": "Reef Bootnode Validator", + "id": "reef-bootnode", + "name": "Reef Chain - Bootnode Validator", "version": "1", - "description": "Runs bootstrap validator for a custom Reef Chain cluster", + "description": "Runs bootnode for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", @@ -4312,6 +4312,24 @@ "evm" ] }, + { + "id": "reef-rpc", + "name": "Reef Chain - RPC", + "version": "1", + "description": "Runs RPC for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs":"https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "rpc", + "self-hosted", + "evm" + ] + }, { "id": "registry", "name": "Docker Registry", From 41920f451cf1fb562b85d6c05399d5b2bdff8b37 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 12:58:03 +0530 Subject: [PATCH 04/90] fix: meta --- meta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta.json b/meta.json index cbc54386c..aadd7a98a 100644 --- a/meta.json +++ b/meta.json @@ -4302,7 +4302,7 @@ "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", - "docs":"https://docs.reef.io/" + "docs": "https://docs.reef.io/" }, "logo": "reef.svg", "tags": [ @@ -4320,7 +4320,7 @@ "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", - "docs":"https://docs.reef.io/" + "docs": "https://docs.reef.io/" }, "logo": "reef.svg", "tags": [ From 72b210923629dab03d60182b699b15ee5b5dbd1a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:10:02 +0530 Subject: [PATCH 05/90] feat: toml changes --- blueprints/reef-rpc/template.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index 422b1407c..b8588a61d 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -24,7 +24,3 @@ env = [ "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}", "BOOTNODE_ADDR=${BOOTNODE_ADDR}" ] - -[[config.mounts]] -filePath = "/reef-data" -content = "" From 8869a34d506645bab2bad417fac2d5dde5a49380 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:14:57 +0530 Subject: [PATCH 06/90] fix: var in rpc --- blueprints/reef-rpc/template.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index b8588a61d..e8e2bbcd2 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -5,22 +5,22 @@ description = "Reef RPC Node template for Dokploy" [variables] NODE_NAME = { description = "RPC node name", default = "rpc-node" } -P2P_PORT = { description = "P2P port for the RPC node", default = "30334" } -RPC_PORT = { description = "RPC/WebSocket port for the RPC node", default = "9945" } -CHAIN_SPEC_URL = { description = "URL to fetch the custom chain spec", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } -BOOTNODE_ADDR = { description = "Bootnode multiaddr to connect with", default = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" } +P2P_PORT = { description = "P2P port for the RPC node", default = 30334 } +RPC_PORT = { description = "RPC/WebSocket port for the RPC node", default = 9945 } +CHAIN_SPEC_URL = { description = "Custom chain spec URL", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } +BOOTNODE_ADDR = { description = "Bootnode multiaddr", default = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" } [config] [[config.domains]] serviceName = "reef-rpc" -port = "${RPC_PORT}" host = "localhost" +port = ${RPC_PORT} # <-- integer, no quotes path = "/" env = [ - "NODE_NAME=${NODE_NAME}", - "P2P_PORT=${P2P_PORT}", - "RPC_PORT=${RPC_PORT}", - "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}", - "BOOTNODE_ADDR=${BOOTNODE_ADDR}" + "NODE_NAME=${NODE_NAME}", + "P2P_PORT=${P2P_PORT}", + "RPC_PORT=${RPC_PORT}", + "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}", + "BOOTNODE_ADDR=${BOOTNODE_ADDR}" ] From 35bfe1ccf350244cdfa13f45576ec1d799a267db Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:18:40 +0530 Subject: [PATCH 07/90] feat: minimal rpc --- blueprints/reef-rpc/template.toml | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index e8e2bbcd2..251add9e5 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -1,26 +1,19 @@ -[metadata] -name = "reef-rpc" -version = "1.0" -description = "Reef RPC Node template for Dokploy" - [variables] -NODE_NAME = { description = "RPC node name", default = "rpc-node" } -P2P_PORT = { description = "P2P port for the RPC node", default = 30334 } -RPC_PORT = { description = "RPC/WebSocket port for the RPC node", default = 9945 } -CHAIN_SPEC_URL = { description = "Custom chain spec URL", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } -BOOTNODE_ADDR = { description = "Bootnode multiaddr", default = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" } +NODE_NAME = "${node_name}" +P2P_PORT = ${p2p_port} +RPC_PORT = ${rpc_port} +CHAIN_SPEC_URL = "${chain_spec_url}" +BOOTNODE_ADDR = "${bootnode_addr}" -[config] [[config.domains]] serviceName = "reef-rpc" +port = ${RPC_PORT} host = "localhost" -port = ${RPC_PORT} # <-- integer, no quotes path = "/" -env = [ - "NODE_NAME=${NODE_NAME}", - "P2P_PORT=${P2P_PORT}", - "RPC_PORT=${RPC_PORT}", - "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}", - "BOOTNODE_ADDR=${BOOTNODE_ADDR}" -] +[config.env] +NODE_NAME = "${NODE_NAME}" +P2P_PORT = "${P2P_PORT}" +RPC_PORT = "${RPC_PORT}" +CHAIN_SPEC_URL = "${CHAIN_SPEC_URL}" +BOOTNODE_ADDR = "${BOOTNODE_ADDR}" From bba1445886816e8d52ccaa8a51de3a50787c2b93 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:21:53 +0530 Subject: [PATCH 08/90] feat: default envs --- blueprints/reef-rpc/template.toml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index 251add9e5..927a1a904 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -1,19 +1,19 @@ [variables] -NODE_NAME = "${node_name}" -P2P_PORT = ${p2p_port} -RPC_PORT = ${rpc_port} -CHAIN_SPEC_URL = "${chain_spec_url}" -BOOTNODE_ADDR = "${bootnode_addr}" +NODE_NAME = "rpc-node" +P2P_PORT = 30334 +RPC_PORT = 9945 +CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" +BOOTNODE_ADDR = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" [[config.domains]] serviceName = "reef-rpc" -port = ${RPC_PORT} +port = 9945 host = "localhost" path = "/" [config.env] -NODE_NAME = "${NODE_NAME}" -P2P_PORT = "${P2P_PORT}" -RPC_PORT = "${RPC_PORT}" -CHAIN_SPEC_URL = "${CHAIN_SPEC_URL}" -BOOTNODE_ADDR = "${BOOTNODE_ADDR}" +NODE_NAME = "rpc-node" +P2P_PORT = 30334 +RPC_PORT = 9945 +CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" +BOOTNODE_ADDR = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" From f63563fe64cb0aa7b456edd8a9733435b397f7ad Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:35:51 +0530 Subject: [PATCH 09/90] bootnode toml --- blueprints/reef-bootnode/template.toml | 37 +++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index 8a719955e..3a1a98110 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -1,28 +1,23 @@ -[metadata] -name = "reef-bootnode" -version = "1.0" -description = "Reef Bootnode template for Dokploy" - [variables] -NODE_NAME = { description = "Node name", default = "bootnode" } -P2P_PORT = { description = "P2P port for the node", default = "30333" } -RPC_PORT = { description = "RPC/WebSocket port for the node", default = "9944" } -CHAIN_SPEC_URL = { description = "URL to fetch the custom chain spec", default = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" } +NODE_NAME = "bootnode" +P2P_PORT = 30333 +RPC_PORT = 9944 +CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" -[config] [[config.domains]] serviceName = "reef-bootnode" -port = "${RPC_PORT}" -host = "localhost" +port = 30333 +host = "0.0.0.0" path = "/" -env = [ - "NODE_NAME=${NODE_NAME}", - "P2P_PORT=${P2P_PORT}", - "RPC_PORT=${RPC_PORT}", - "CHAIN_SPEC_URL=${CHAIN_SPEC_URL}" -] +[[config.domains]] +serviceName = "reef-bootnode-rpc" +port = 9944 +host = "0.0.0.0" +path = "/" -[[config.mounts]] -filePath = "/reef-data" -content = "" +[config.env] +NODE_NAME = "bootnode" +P2P_PORT = 30333 +RPC_PORT = 9944 +CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" From 297169572debc0c49857c44c453ea59f97c3d9e7 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:39:21 +0530 Subject: [PATCH 10/90] feat: icon --- blueprints/reef-bootnode/reef.png | Bin 0 -> 10211 bytes blueprints/reef-bootnode/reef.svg | 68 ------------------------------ meta.json | 2 +- 3 files changed, 1 insertion(+), 69 deletions(-) create mode 100644 blueprints/reef-bootnode/reef.png delete mode 100644 blueprints/reef-bootnode/reef.svg diff --git a/blueprints/reef-bootnode/reef.png b/blueprints/reef-bootnode/reef.png new file mode 100644 index 0000000000000000000000000000000000000000..c45c9560e777c8dba62f2bb225eebe454afb955f GIT binary patch literal 10211 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 literal 0 HcmV?d00001 diff --git a/blueprints/reef-bootnode/reef.svg b/blueprints/reef-bootnode/reef.svg deleted file mode 100644 index 598d7930a..000000000 --- a/blueprints/reef-bootnode/reef.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - diff --git a/meta.json b/meta.json index aadd7a98a..0952bfee0 100644 --- a/meta.json +++ b/meta.json @@ -4304,7 +4304,7 @@ "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", "docs": "https://docs.reef.io/" }, - "logo": "reef.svg", + "logo": "reef.png", "tags": [ "reef-chain", "validator", From 3e19c09b1ac82f9f464ab02882056ed9fdae0b1a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:45:02 +0530 Subject: [PATCH 11/90] feat: icon --- blueprints/reef-rpc/reef.png | Bin 0 -> 74223 bytes blueprints/reef-rpc/reef.svg | 68 ----------------------------------- meta.json | 2 +- 3 files changed, 1 insertion(+), 69 deletions(-) create mode 100644 blueprints/reef-rpc/reef.png delete mode 100644 blueprints/reef-rpc/reef.svg diff --git a/blueprints/reef-rpc/reef.png b/blueprints/reef-rpc/reef.png new file mode 100644 index 0000000000000000000000000000000000000000..44cb674b1f45d90651335a1cdd41d2e3bc5cfd62 GIT binary patch literal 74223 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 U_nkbne<)GFiW;CwdD!Rw0}V=DFaQ7m literal 0 HcmV?d00001 diff --git a/blueprints/reef-rpc/reef.svg b/blueprints/reef-rpc/reef.svg deleted file mode 100644 index 598d7930a..000000000 --- a/blueprints/reef-rpc/reef.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - diff --git a/meta.json b/meta.json index 0952bfee0..48f68dc7d 100644 --- a/meta.json +++ b/meta.json @@ -4322,7 +4322,7 @@ "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", "docs": "https://docs.reef.io/" }, - "logo": "reef.svg", + "logo": "reef.png", "tags": [ "reef-chain", "rpc", From 61bab6239ad28ce7b6d843eb9ff00e01f7437176 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 3 Oct 2025 13:54:02 +0530 Subject: [PATCH 12/90] feat: old icons --- blueprints/reef-bootnode/reef.png | Bin 10211 -> 0 bytes blueprints/reef-bootnode/reef.svg | 68 ++++++++++++++++++++++++++++++ blueprints/reef-rpc/reef.png | Bin 74223 -> 0 bytes blueprints/reef-rpc/reef.svg | 68 ++++++++++++++++++++++++++++++ meta.json | 4 +- 5 files changed, 138 insertions(+), 2 deletions(-) delete mode 100644 blueprints/reef-bootnode/reef.png create mode 100644 blueprints/reef-bootnode/reef.svg delete mode 100644 blueprints/reef-rpc/reef.png create mode 100644 blueprints/reef-rpc/reef.svg diff --git a/blueprints/reef-bootnode/reef.png b/blueprints/reef-bootnode/reef.png deleted file mode 100644 index c45c9560e777c8dba62f2bb225eebe454afb955f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10211 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 diff --git a/blueprints/reef-bootnode/reef.svg b/blueprints/reef-bootnode/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-bootnode/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-rpc/reef.png b/blueprints/reef-rpc/reef.png deleted file mode 100644 index 44cb674b1f45d90651335a1cdd41d2e3bc5cfd62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74223 zcmeEu^K)iR&}eMi&c@!@-f&|ZPppk?JK5N_ZQHi(Jh5~4{i<%=zu^A%)l}DX&rEgK z>C@daXS(ZzE67VC!r{SzfPf%M{Ss3G0RfHs4`9IljUaF3W&Ge;XXs87U#D7cv0p33V0Rhhi1p)upgZ?*`3-*6!LF014{~!L}LVOmx zNe~br5GgTX6*th!E{jIfp`;yLv+jco$7u>F01Ax15R5;CGpzT{1;gHtoki{l?)<+QkO`n)fnP!Iph7R`^5D-Ty3&7?A2|I8_e^7yi=PIDj;ukh45F&1 zi8teu15O{wTQo%b&fabG?Uwy{pnd`&G*o(fsW}7xat8cOVs5D58#MxZG%(dJ$n@u0 zopp02E1L$fXg4@>ehNRlf#Xc?z0zkbvB=xrD=ksA?2^A3!~<3H{Oj@X!kLj9u2YJJ z8R*9zg6Sdf*#U7vdL*Pmi!l9wUhbCsAZzba*ibhbQ5eBab`7^jVMK^{$~A z6C}lF3Re&ORO+P|+(T%)fcAy^Q&mqY2rrb4n`A1bw{%u6+xa)LXpA5Iyv|XI#5A#s zk?RM(UULlKWQ(tR>C*wjEl~kv&ZNLo&Ll|~J3$=kUCy_fwo$K235C25+GLOSb{I!D zIT#4{K82{taWO+ZK?}rr)+WGU)|{MAs;8!m2Lk8}L6r!=(}`X;jNg^uIiD=}t%%jR zmm^_tUa{^j+ko2Tt`5aKv!7s5k5@F1nY3O>8RPk%8k)|MAj_+i4Q_*}c26I4u5;-tQOVrV{9-8o{~ ziIxV#a4g0y?*HDeYC6J0ZC4gnW7n3JmkF6ty}4;H{kt+qaRU}1J@f2$Q3|Yip}TuQ z|4T2vdGO{yF!Sy&^&~DRr-xthW|*_9H}RNbm-ttn3}Kr1-Mv!tDKsXb^}i?<6AFtV z_U9lDh>c6}a?};waeq4`j%%on3LJM6KyGJZq&oHHF9}s_%D2pwK#Dc%gOj+xDM(|I z{VsA;-6>~P2*%TC5F45C_aLBW0Peq&!oT8Si z8VVmtqW`4{j{BqG98~|NPTxe_r8Jpo4(_7YQc=0RDuVnLuV)PKKax!_+`_9_Eig(| zDBy7dh=dXI!^Pp+TP?}O<)vJurS~)NO(^DFoiC^{?;NVyo8FSc783b}n*x5@+87LR zzfJ(C&F{Sm+$DCzqEqc+xC^kg4I@OwXn$a-3jKkFgZJd2DiAOmxgqaiX_hXk%w$>~ z8Yq`Iu|3pF`S;lWg!6+XAI97#Vlqt&vVUP>(GbB(p#%wm$70qq%rynli>m(2L>icF zr%%WJq>ij^usU1HAJD4GvUW3cZ)`m9&(l!9a09h4m4I^qg zIh8vPrsu#+*1z@5Qg)9?w4gD3M*zhsL8bXQ7&F+vO2SpDNcssF*Mpt11Q?C+oa+gIH8c!`5Eb|bhfM&8WDKtWW#;FbN-}mq(jcsB;wMR|Q;4b-6^VcsuSq}f47aw!Q8Zdf)8Jt&w0f9D*$Qt(_7?;>(3|spIzQ9Qd($-Amd(t$>RMbE#j4w>Yqe0hRKLc z4@08ZA#fZ)>aa$@I{7B76iYe(scyZrRq0Ujs?upE6HRcV7f#EkM-B<2 z5-|pe#RCH06E2>3y&^zbCC8^m_v5mp5@aIYSFe+^5_v=(Jy#nQ1Y_mF?y^wQc@3~A z5J}dcvFn&L1QyA0J<7f{7qnE>^^H+v)@#r;l&wh;ruKq*eGgsqC$;66DSC>I=VsTN zoQ~aon-rb)oB4_Tvo4On^xKNj&|q9B;j2tmqsX$^Wc8#^qcULhV%`HGrsEz;ClDU^ zJ7oFY4*tkWI;9UkNL1=|6yX|Pes1{eJ=Pm>PpJh^=cHBLtaTW>n7z#D_f)f(Y1yFv z07(Q|b((QawiEGGdtHKC9^q2(uD#S1Q2PII9aG$$lJb{=rq2bJ3TjR??4)PS1@8$W zD6+!{ey}B8_{*Z}_*BU4;r2QSysKv7>#jeos&u;ZhxMA2uj;jeEEYIT`J%p)1>=G6 z^7JPtV`jn>orOLG5lCGZ^)LcU&@UihS7Zj{!JUef_M@oA1d!nuQAhQv`&l4@<8i!z zeBh;nY-lE9cXgf?Y>U?sQ+!^Zu2tBrK3_j~9-Ox{9lPKrrv7B>+S^~Ww$VV6z5%(IT2t4p%IiG@S7Q;H1||HZwB*%^5}=5O1+IU5uj+Ul@R3Q;xOd{kI#;x_^VV-|x^96(*>X0$sq;*wBw3&24~=o?dM} zGd>#DzVU|-ok1S+zKcT4$M)z#IbjCAkSZ341*p8~p;aIJ(46Qa__Qn+SDROfE`JaCZmy_`ihptrQ0D-v-%EE>^ z^_JS^{8U|#o-{^IyV-sm?Aq~uTg|iGwp$)p)8fM3RaZ42i%n=H5{C^-%wxwg4P(zr z6DiXz_0qy1*Q^qfw~`J48$j7NnI6{3S$17GQd|LrXxuZS^ffd zXiLd4B0QM)KBMlOp<_$w^ETj8vMNTj_Iw;tpA0b zs4_G%UO*DR$RDYYxoC6YHv}qCtpYjRCWCE~w?g2jc9P2|Z8C9R8TSm)PIRYK-x*=n z6>c?9xHk2A65%Q?D|A40rrgtFLD+Aa^R?c@zh$VnNQ zJsXAA_Rm|P@_R4fYbzrIIx!S3P7{~CvOEQb3p;m+moHke-2(SebQy^e=e>&JDSXbc z2dJq_+|BLBH^0H}r;#q`0EP$MJ*3k@O7mMLZjMwpa1d^TKVbwuvCY zn21f{XM>s|cpd!E2=*x;U;K<07JEds>+A> zc6ybH6M`;eLxY|IB=VKtFS#z2T7*~ygo0s{rMYXxkb@BI;l%Im%R4bdT8^3ze$#zW z%Qht3^osINg8a_zGZ2(j?_V~+$Xa(X^w=O&_qsLiu;RTP00Htmsu@?cz@Z33f5?ZE zhbe>(hB{IAqDctXu7V|hBKE+N{W~a@Qs06ZewdE!gIts(mL|C5nrkAZs@>by8@(Zp zjlzG{OZ151ixwH3GyF>ZqMIZUVst+@yuYg9cbRd`;RDLz#b6a%dS(FmiWz)^n2WyP za#dnf@a>KfQsCfqo>J*7ar$7j^dQYyO${ya>fJ^n)Lr}=thL)K`KoFMIn`f(01v}n zg?l-W;pxiqW@-G%WKqYBlbpHxk^K==@A-6?`%%IYbt9km(`JMPugImRZ>qDd_|rlp z1s;Gco4|_|+Z`*V8bNwbwg%Ez;rhGF$=r|F~>A4>N}GXxzs@1Z$vFStxO z>l}`X`&0P{$D*;OtWJ7jr0G)L8oOjnf6_*+k(2c?<4vsT@*Qc_uf<#YsTp1AM8*wt z6JEfrRc&;Sn&++!u*^7aWlkhbE0)DhL4#+%OxhEyIw?4vy1=4i(Q;4W=&n@#W(q5b zmCThlfo)+7G2BD+k(P=)=M-0pI|SO(NP6!vS3O^2`mJxv>*vy1R!I(sO{&3vbAnSy z#98h9MkUf5B*>Nbn6yZD%HJj8xBLrwO_X=pK`I^Wd`+E{?2zZu33lzIIpZczZ0;y(OfPMCjXm>l6n;H{g+?EXGanV1stMfrW!M#QY z$>F=Bj1PZZ=O+6_K62w?xfHT)7D)K~YMBulCxPqm#^6rt0x5>eptYcrN;pTrrD5^} zA6?c5O^QqPYV!^-=$DN{Nt)`Hq+F&E0dJ;U{2f7siDrmmgFeIzGBPflTts4M1@JJn zS(2GwpS(RQcqVMk{}TLa(C_~E;rMX4miF4pjX!a+1dkWpW|*@kRaN|80$HH-s~7{O zf&j9CsNwBu9zOw0Q29FmtcD5m#>^&R1>d0TWBEYpnDR3&o!nYD71*_IZ}Hd7u-WKq zftcw1vgAfy-OTFfnnI)3nFJrhLJ+yD6?Nqt_6sgO96cEXR0MsDLr7I&4?UH#cVF9M zXOziu%0A9HqF(uJp#Jj2^fxuyn`kG|=)i8v^kH_goTF?Af$`dYY6Vy5_;|4S-;AvL z_exlV(poxj2SP-v)h%8tnjODU7zZSXzvEaj(rMdAzkkC` zS}AgBpBI%RmVG@;e+j4zmS zcCpaxf?TNDuH=O0ZqjG)s##W$8a7Tx$JV&VyDTX7ELY1ByKjE%eLLpTu(~9 z5?{Ccp-j&^y@0U-F}d=fNH67}BSiqi&n4mSny`doIvGZdG=G4>45iE&ku)aZ^F!39 zE;qA=+ z;WQ9>!FElPj;CU%mKalffU|1$XA{?4&4-HS5`B(h^A@RxSbBRM;n14>0uj1_(QJ0<_+A>b9ED=jQfIpjcQM40rzm#8#P5<+< zPVjy4Sv`hByD!*~1>b$a6g8H0kSQ0=3aKBLMcbb288sxX%A6P?@9!=2sR4&6{TQh+ z0f#2|!6ST+nV>E@!Usi0TFuo~k9}V-uglF^v*XO<2ggD-JA}0PPXxw?^H&p1ZwejF z?jyf@l5vF#B!>aApHp=MU7~{AuMdeO$bubS=P$Vj~#~hc;vDGJ?kz39dx+ z^4-|~sfx_rN2 zP~5!xK9CL`wHuE#aq*Fly_>I>4t*a+-E8;}cxPV@B&$lBCs{;q1tKMCztkXw1#1z( z0T(gO5&GztfzVaNM*zMrqgQHueakxU_RR00Dq%-~-|GWbc6|0r+RwV)TO2}gONb0| zqNtGTAU~uvNmW#J7;V0|s{`quEA>_SpOVtqN+t)MtNH)z%-#4NjOA}2OuP=tPKa7z zpcF!3Oi6MXH+`_;)jh(#I~pYdbBm=HBsGy8j%+dX`e*g18x%H9l| zH-;UYEe^7y&Ger_qIp{$cAbA1CLPtT$nCc-oGn_ii^W>wpvs#9TVDmg*3@GNCKrm< zTw0lMnoyMNZSatw6g37$<)A6^;Koy=<)Juo&3BF6@BpIKY-b@=<+0^M^SBG7_5nFg z84a)gbbuY_4OKhOlEO@n35V`Dri2+}v)tTS-*^k>hJ@T2At3h)|R$bCNH;aJhIAnAZzWlWG{pI_$ zy0MKJle%2_99~3el{6D&S;+M;TL-2At--Y0z`6Vz*Vwd)k4k(2dJ&r=Yfa}M2TCL3 zX3a}ytLIhLxwDO_^W}UwYt6E^JXd-LlTcp)eYD*{Pa}qacidY<^vECgrNF=K1i#?- z=Y^f%Q}4L-)m_>#Q6-l+$6sxpym-k1Eu9)Q`VBL>&(M&5Z5b?}U-kvtLNmu5Z|h*n zwt+#6u3J*u-m;ACXK=+60rM~;|uI|c^ZlP42Ga^L!Z-##0wQ(tpLD0zI$2yIhzSoEA11(z) zK*3kDo2X(rtf~-p9VJCy;FxqNbR!0QtkfM?c}I>N4%{qkqx~b~qdzZaB{+V{vcD{O z>Z+?Qix+SvT9T_a{pc9onBDRI;6!(OpSVx(`{dh;PG2$pjuJ+ztC5WC5T9yDepIAp zTmNB!{eE`qc;~yKhS-Cm$7E$H5OZ?2iEN1VFT3S06uvz=``UE)#lR2H7SzF=_7 zsDfDw>gzir&Dabk90}8;HeCA?NO){`Q z__#RZX{c(w;I}~BBM=Fm97D}l<&dULA;WO}-v5X7wLOBSxhW+p`3oFQNMIjI2lg`| zw~yOMb|h0<0AvWnOJeR1p!9yeh6Yg~Sya8#8KN}kBmSGn&|g);D_o$_QX2%VlsNz5>KLF0m z%k%?W>#Q3;?LJ=5cD{Rh{#H#-QKtM1uyGeG4W0Vs5Q)wQF9r}9TSO-)px4PmnycV` za40vk5YcJS&~H}K*nP3`B%$UP?I{Vy?;LbU2o4tr{+rJXUTo}_ zws-@!*PrW{c$lZBBOdFm(coE$NW%LPZeCsw0fFZyR!^UJplYhbrMCfrQ;&z`>OTtd zV)tI_h|_$ZYrggu_vZX{)m^8N8uawcTA-F|sRO_a^DrDDe5NY#41`uxN{V|dw-Mjj zt&E`tg{ZPmW-}}0s<1h9b)3f++5;0gp<tZXp;a>*(5Y90(!AB)bA?Spoil)QU z2-&dpw*d}CAn@L``7|z^HD-kM6e%{v4ZJ?!ib62tPukEJ<;`;w<;Giv3omF<(R{&B zq`2kM{l(|MwqOoQAS>Vo4UBn}%2VuTh9fzUhTS{We9LiPsyy*^ZDk`Pgc6h)+E?KY zOpzF>CddOoWTe9M6;cCZQ+>3bdzrkE;-vxf-ko|RA`EK3R5US>42&HmwUh9 zvO)L*`#?gSE=n+G<+*-2uo{H3+bV*QV`?eebxt8JvQZDK4RTc?s4I6a$hNo%F5~_OW$jkzvU|3=1-2Ux-KxYsY%h`9T$80Gwmmw z^#dWC?b>@VrqE!EjHIC`5~hA(LO8u~g!Y-C(RTSxA41f#*RC$)buoA^iA%*jTseJD znV`j&6!Y*Omg6esL?BilSxVgV4AZ0pseXMB6FpEL_^bw=9AQ=lHB~v6ZcCRyYPu^u zUEZeJUmIi?7!Wwzb~;B>ri;dJ~H$_M_Gyj{vigO%w=+ z$ZH^e85}4ow9YifTwRB~Y*{gPWioTa6X$#9D&Td%^fF;F2V6LoO-Nx;+@UO?Ccqqk zoWNXj*sj?4h08GMZTQ zz`J1Hyu#B3Xy~#5*fKCkTAi)mul2o8^18+uV=aFEltaA?u}Bw_%LIV#JAiKGNacdC za>1%K?1J2=+LQb!4Vk>B6LvL9g9z}(Bp95=jzIi06zE04hcpZzNA6zGbxANTvQ)Af zy2!LyrD$z1Urt_AlFsfPzZ#clU2#bP`hiP-)ZBCPLc^C=RT+ZdMK!+G2b?PhZuzVaq~z|7#PbrvBHh zYYz8IL4&C)Px;JWafZ;*5ESR-29S7T@CFlHk!KORiS{iY@)$F~$5K<{3innjuG8H~ z#=TX}OBJ%-Gh=SltRki3_h+X}&4`_hM*&xW2As*rU*;VQi@cS(+4ZJtZ9)54JRLwp zu*_0%pSoS`pP(KG%0>0@;L#g@2{O9?4u^&Gxw15uQfxgYjUHm~#D>Uf%Qg_*olJ6)Z)x?)AyMdy8=S*4U)st&DN;{Y)qt5#a|O0h|8atjasBc_8=;(Qmk)JHBaZ9b8{-e1>HP@r~# z$1I&1oGB)ZpB^%@@+Uj_D@0Z*dQ4wG`*Q(2S_D8p)Pz7 zjFxHDLoZ=i1Cn+C^|>NVzC9M&@&pBiWx}>4`g)-g|Jdbmv8#>eOSbopJ7A0sIGHZF zFchhRZ4D!IL^nCF5Ry8~Dn(=(AvAp_Yx4#SMCS8&TkO`|K={&cf(pj)HP#+dt1;6} zCxGkqZLYGjGFi`1-IsHCyY&%6&B8!Y1#s;?$Szy3|lG>zxSvV?Y+t>CgSy z>Ass?6iV!N`~qBRDDgCtuR&}tFj$1?l3=i;BE47Og&w$;UgLdrdOMAk-`>*Wvp?Gx zS7-2gL4R-2pY}`_W2BKF+@3ecT5n$k1>fj%a?IiBiK}_4se+TenLesDrncR_)Pt3mIMuoQNCN2*1GKER zI}eU!x?hKQMSU|!G-ktD!<>=h>ibit;1=*nC9DjTc9Ef`NP;B03RzqMXKR76Ih)Q) zNMng6mG;FP@8x1A*aM#)gvubt{)BW`^_v{Wb<(_RD!1ok1vGmC8emW(6w8FrqSTew zEfMUQ#Z&7R_k)3FpF4sNhXW(Oi7${ngsZyakN{e3oC~E?hWNcoFF!Pw*W6;pN*9sY z@<9+*ma~1BJ+EgIFQGraV)f+xP?Ue%ZfLo-@i#EzJf&hM6p0}~T`1!Tq+7463^H(U zTK4A8D(pzOfkOZM9jg@cm^nKjhe7h=*sG?_qn_9#TJWpo`Gih+>i9`fkvKqui(xRD zUiA_Ti#GI+1eYM`jT`x(ejL*jkHf%3SCO#C%ew2;SH}0QRxD!n#FDv-r5Hh{j{tks zk^2C>af(VLNRm9qFyR;vCP1oxeRHA>^d4PSp-9>{$egQnTqQR{cn^rm4rzMn?s-0> z-x-p))Y4_?8cy3yyCZ;HO0n`<1`ZF9eYpcH2O> zOyqkw%0Rq{+G64;y1sw_CRELBr#*s=J(T6Ht0W}hw1!BFG0XWOEk*a^z+lq$Tn%ry zkCwn}^=3XQgu`5sVblsnt&8or@8+@F@#Nom+o`-I!r7G;saftdy0(-vTvVhLmBWg* zvr_OJ5r!*apP6A5&N$>w?Qi48z2^4KQ|!*`2{N&O$D!T#C-P<2>dT`CxGI&YX7vNY zhWeoHQkbRZq67rIFI$m zd}KmGrCmS>M)i&h&*qta(9`>8dGq@;4aN}>kx72^+6maneV;yE$_Dg$szZ%YKLP4l zb>@_%^|JKxfq4=I)Wn6gMim0^>X}~6Zs+ksH$G|Rq(k8YBOa1W0fKW%ob&wga{Ml=5wnHP#M;$s=%u=H_S4?pfqtEAh_3=bWblCDZ}n#h#awD zYmy27DXKI5GIKMXRm~ns`szH#9dlX4i9|N^tf$JHMxk$8I z@d#XbW%}?2f<=Aw8fUso`2JPrQP}y%G){j|e(SmpTz`L*JR|Z}R4t1vi{b?Lxd~#f z=oH4u$H4!(;VW0q=i3+Exp>O2-O|+JbDz9y)!1`d1q1hL867=nE~TxO`B$?^r?e4* zj$OKHM3@x}&TZHxaxZS!lUV2P&Yubh7yXWMe~UjN_WCR%{=S`zr>fU#YeBuV;J~uq zD|kn#iyq5NdjgxdWARH^UD7e^UH&w@D#;kXjY+0&G+rx~x7geTwMQX=2h$;w_jn~*Z#v^2ayWXJ3L z`*Ya7jX&d(DI#qeuOi*S$v{MHrZxvtw`S#HNGpmAPgT>VH#@SHo$=0 zec`fH$|2_!h!N=$c8fpkzC9J{Ud58-^6pTe#4tuMftM4pjR|c%DzhY$8cYsSiZjTH zvKt&z9acaK8~E2-rKP;^DcNDqX#{>A%0LxO?NNEJ<4zdV1^jStv3aYQ|0avO17}7z z!KE!;eQfg>vaGUZQjmlNDU{wVNT8Jz8Ol3Z4f#!zb!8f=>!_j9@0bX{t&yIp&4iyc zStOMY!dIqvCyuD@j;s+pr&iJ*nH$`tqYX@7Qa~@&3L&_$+QS(Eg0bO`U-T zN{7zbj_${SICcW-@CH+H8N_rwg8y5u2@C7%KscjWdTT{ic9@t`{co_t>oBr?1pRDl~@8;}Ul7R=8=f zdC7<6jnpv}G=<{9b74_WlbFzPoBJ4d)NE-n0*4ZT#^57(-{qDNM1aOQz${slC-NBF zGaNEWD zW){xNA;IW{uECRGL5o@i=?!Z3>k>ABOoL@u%Xe)u4P&955HNrn;LxvkCJqF1I03EWIHsMqj%y+WictOqtc=lq&4*@!c{EA$Kj2?L;zUFtg(NpD;GHVirZiH~O{YI~8HeGmmm^6z%t%1oYq+jWd^HO^VWfx6aTUIN;h!d`^YDMN%VNmV&+T7q9i4{ir2_<}eoEe`1b5d>I>83FP*Y{yX^&9^AfhsBZVhiv#2uL|hBY|9OaL;8y z;N8FZ{8@76)3G-dv>Lm{4K+jYDC;ct$l2-i3U8y)U2-%UCL$WmPs{Z#oBOZQbmHKq z)AsH7nMypf*ffRE3B{*SSN&6M4*L@J6N^JrO+k>DDr@n!=*ZWDv>Oan*=~r0uKj28 zN;Rchb%6K+1Qi3+#6*ROk`;<4a!fA4UkN=_wQ7+l3eF@K{^yie87X0923L|<^MS?^ z%b`<@YO^O6J%u)J)lA{(T2ygVHcfi02@su3rUf@HgQ2FaHd!KWuv6gZGpo3LioPI5 zZ6F;YCqVux&gdla-qy~>#lyw9$?BsOs|8jJ>>cqp`cE%lb`AgIX<%e{k7IFn&8-#J zT`jo>^oSD$?dB$CBz|o`!R<8=ItV)67G%Pt7WIIq!rnu4NNj$VG5c6hof=lK%Y&ev zivq5s%V2Nt{7Df_PMw2kLfr zX(Yzzf(4!$wd93$w}Thfhqzxtate^{D^Ea{Li6zi-b0-jf~acsAOJ{a2zwAQnklY_ z_==N9nBWh~#dLx5o<7Zf##*vNzM&tI_F}t5l5uroKupS)KpSQV&3`4jye@sDfFK6a z`#{+!t!I;pWz|<2Gd-z2$h_tGn&-fyaFUda7v#e5C_L4w{I`gReOr%Dh2Fc)^%fC2 zt_YCMEcq7Q97AFCn@Wqtsk{5*$Q++HZT%OixvY#TUA%Y4y)YOG)UPZ0!-UD0n>vp0 z{wPKZc*eYuzYs^wdt$ZPeKLBf%$ShSQt(01Vql07E-KIw%-|CYN!0PeHZB$x4-W@a z+s}J8T#C8jT2tXA!>6WYBT^Y7$?<ZjE49gSHqiz{){|X zaFiMPgB1Vhi;RoNC{K?}$ER`xV6!+mNZ*HCCN^-D6Z+0D={X)$JqioP;%T(b&bvcY zj#Y(Gj#rWY2z4n{jM*hIMk*Z^cSjyn+|<<`NI`lmA2|*oXP#?^A!gVAUXT5qC6Iyn zI{ES7%-Lyrrpf(p-^Uen}@&i!dre`qKz_i1WfFAy$Gl zY76sy2qg?c>t$;wkcWfO-xa;5xUfWqWkq}S0<|_8Y^dVoYSB-vAe>}lyN-%wI*V3d z6fy!c@_5&{P0oL(5gG z+xtd)K*B$1cH;V%Ffr)(7WIBj(ETpkPN2nwXKi7d$o+~B-zn7l+KdQpPIA+)P9%z{ z*^CiohWHJ$qN($bA38D6ZAgqnbGQ^*evTB#SvX>_UWyYVYeHihDO=f5>4#rb8oIFR zn1VgkY{9a^!#`+ulHjq}reE_mX9c2x2@;MIC)&M|@W*6WOTajW&$W3imE2kn(TI#T zSCI|!M<-9psWVnH3HUvZ0+A8=JDG7ysKmnN*IdQ3zCY0@0!irzM-laFBfFgKmGpz7 zlq#3ue7vR{V?5m6#Ck7^p><6&24I-NFy&Psi@@J0 zPOd@9strT!XehoQGRt6k|Ei{LWjekU>#j<~iT#9XRavZ8uH1-?L+9Ug&ugfD3U zAunP9(ywhQfdmDZg~)*#@7EF6k2`@$Nc!?Tr%QN=Y`i`NNq-Vd z?Ym}gLjA_mf4Owq&EZ9q@CYO0R2eX~%d~B+y$19cj?9tw#Hid5q}gv;Qk|1aNLG>c z75h?)GsQtQ3|Z!cIs_Ch7k^{8he@1TJ%s1y$KRlGNb3;q%!r5^=MYmFk81%mIMr!8 z-z&&l-&rSS7bybwFZ(KvW(5N5r-%mOfQE35=ohhw1E?<#b8@A3J_*MMF=i?@LpyF> zf|dpNr|Zs#`7GrIN3)qqf%}KLg%NeR?ORO~CE2kMD_nVfi~weZF$~9L>oAlhG!ABc ztwE1Si2|d1rPX;dld$_s;IfYmy8)=WtOT`J4KsNA{=DB7m05q&*XG{I9N2&%FM(Vg z7~4a8@uBFHX@};GSug6loS#x0wU*WuBnzWP#T8^e8^Kq{_wGT%h8z*NF&|DpsP;`cTFV3;z1^cZC} z-9b38FP6TOM@a`p*9kt~&r*;O`ERBi7(-Z(KD&ibin&zYL*bB(*8?PVC>B4xabE-QnVujD(Flf1m)KBjqF)`*W& z4QlDZ$mkju+p_FU2%<#oZ4Ol(;Wo+!Yr-Q(t8gr>agateJDvFmOE z!PyJAIus^LHjfEtDqFL6f4L+i?)u1Y+2w*0zr3tL=^a=2!%Cw4=Pwp5&$?j*Fk**G z!I0F*nT2Jh7Hv?E0tkby=sIhC1;DTVejz4uJB0o$d)aRo0p`hsH5SoRBY;hYxleNu zTkMr?5Be@e0wUd3RyEX;3=veHXkB4*+#(sWyVEq9Q-hNj2i?2$7mNy16)Zn7n`y1b zD{bXq7x9byAg4v_b9LtVV=MH~j@)V{eKq#uv{%qnq^8ML9XgVO9gL5$BCO07Rwz0n<3s)yK{`&L4&z;~yKo?zd}_@5L=yk9Z4B++=-C_?JmR z)Ffj>lnOx7KBh_?ALf?Y`^CGu#vPgBdD~Ynn>^0}-kR34=82kamdq`~04RUi+cl}g zE}hKcUeOJi+&f7P3(&A-LH`L@^H>8yNQOR(4)3to7J5QnX^zqM`tL-7Z z8jWJ@9TSIigUj6Ju?CV6X;t7@ucCv6TLWd2krssR8INxlWauUW)e4LJgb3U^vO&sN zw%2G*9v6J*170hkMhns%&+mQ0NN(L-mKN2wpYQ;PzVPCaM zJUi86@}au9-7V#Bcu=};Qfxna33-d3E4J$^U%VmiQ7%B){M`44j-O&L-JZ%01lupT zgq1=*miIhM%AKHB~y$L#TMo+4nOwg5I-mr z2#!YK9pS9C4qqQb&(Fi((;kmo)&>!y=z$+Fu;)uNKH%_& zG?m~7vhVxB267l<0{1Y^OpRluP>;7%{n&Ux;ZjymxY+=lR$3T=u;tf%PFCv$6Vh@Q z{O9_|-B~_&AYqPhkeeus?-MSFjQ?TEy7M4Q*E*cTvbt_RNzQo@4*iwP^n1SUs|vLW zuCgiy7QgwLNw{aX`(d70DqFUpnFv652x`-hplIb+tBO1;26kYl>OV z?b*%B-NO|k~0H#mlMnm6`u-{*?Zh6FcIRyZpLXaF;>5-R}Q=9d@D z<+(6(l7O1`{7reFGDLZUBDQv}`n#2tp|P$+A4fZ0C!T_v!=r(SzT`BH8H+7L zC>iLVUi3}5NMs%wr+##+0r^C)He|V5pV61UeZD)k8t%incTd=+Os*e*4Ge0}pt+|e z|Gum|NJO&o@)&Mbvl;;){B%tI6c9_p^)!1SNgIoX%5~;yCPmHD>!e*AfP<2c)-m=5-;A_NGD~zReGc(W548|jnTI1+Ba|5 zf?*R}ne2)lC*heDb_dJt>!U%&I|Ck#L>|6oY&jDqQq8 zdU!v&CTiAVB_UPkRX_$*OvU5&5_z(r(eW8dTOySo>*63lYwPpW_q#CBINnE)k0p^^ zOAg?LB>wRA#6SLZW+_@v;0{)8m%PcB_}j{l4kt>A^ARd8OWXW&@!rDGr}}zj#viN^ zPfqzU*a5yeMMO>Of&iW6L%a_GpPS#u^|feb%lBN9ZKpH0V-PR0%mCxDy|SR(9AsVT<^G0 zoUR&7wati&`GSie7y^TFa<61EMezAhIkx(8$7R8Ql93Io{s0NkY`3wdd#$`%i1ip> zD>13g`g^6OC1hpC4b@P9VTUFJu_kGX@N3OvVEsV}Q#v5Y+AzuzD|{uM-e_c{DPIHe zy62=Vf2UGzhOG7-Z>bekPjM+|HTSQ48Ci2OT3_Q0ZbEV_C3)A&9pU=4R&MwT40=Jr zF-!mY(Wl4ke_Q_`a#CiGB0YPRl+khU+iR~K)mYug6!!N+XlTwa*C}0o9ou2k3e4Xu z*@m&m=ti#%#cjl%xDkTaSx!}In*>P}*(-+UADrLUb~^5XWj6*O7oI+`DTh&kZ7a46 z%pfVlwi7?el4XB=Oz@z~h#vcb5Mo)AKvD$y_!g5sOFY%~CPBMibB%@uF15YBuDPFK zMI|Y56t@3*XjyG~GzbuD6|S!0&Xoj~og4FJ>J8wQYoYwOB)qW^xIgr#%6U0Io6bem zGiZrgtL{C7TP;bYWnC|=VDvl+zj}xIzRmfHKJ)g|OzA~<%w4;x8;P3aD0FTt_sM!j zqzc2O(4pay5xqc3`wiMb=4+7cgmCrO+_P2Y#}@`KCM2ynNH-H13FXtlILoXTJL9&wT4k2N!Br z$D?`n=SsrH($Z?JQJb1-uXej^PaZ`?5Is4+&<>-bJ6y@SXHv7g{etMG+b(_ghgfb# z>kmt>n`z^cRvgx`xv=z%*R$C<))0-M*ojB@)r5FG_tq4ZNT@|vn!KOXX4i&z*yGWF zC4_BJh;zHzp5ETJ#t`~6U07ppRjqL?HrI4~;nn@K)qq8PtdoHNrE)-!`UFK#(}%rb zTqYm;wQF{5-|_j+eCM$TpNeWbF!W_9W<%YRW`o>x>+Ml78;_-GkL&7@+V+V!EKXq!g&RWF|J4|`AvvHcOU{6N+!oDBG zlyT6SVdgN^_UU9-tJDP;tpZ$-)>4)Zu#RE&_pAK1<$$%R_*Fe@wfwR-T$Od5Mc~`Q zTRyOT_XWT5_yZ@799n(i;bSb}1k_?yF3rqu-@X6BOJ{fOuU-0Grw8{_7?uk9GNQ$K7f(>{GkGYz@cScfM0S&Fgp#ZWXNl&9N{06f)*r;q}Y%06`l}5cbOcgJbX`MJ~Fms%B zMscn7-2LW>hmOuP_b#95HIrFfZvY)x*>JZ&mXaY#B|#|^G$MEwEO;bhy%!|8m$2?{hdPFT3Z*m@EK z89@QepZ~}2x5JCi9`klg&B|Soaa39)&8h%Q{ACsofMdOVV`_Ssuf$W?>CThC_4hyY zOTRLWR%eKnOGcW)Lf_*X*$ppaJcKOE!j)nX*jtt`$#>fRGeN?t)sg^nK8%#5jQ^A= z_xfHmSnD5c?<_CBdXH(!Cs6O#BtBh<8m)K3jashP_ErD;8UWULWu{|Gq48LYV^=B- z*UDi~YffEqqq*erohyqwi|jJwh5@1jHucyv+6J9DoE34A)!5$IGjoyGpwAPmL9=3D z))?Eztg-oh>b;v2^L;7O@D!xR9Cg=M7Vp3NS-*4nR5T9`Y9B;D!<&WpSulr|>!3ms zs}G-X>{MZznNvIW23KCQTg`;%UJY4ew4#6&F?H?SJMExqT~x?Of*3xl-9f+EoRPCV z!gyi6d)G5Wt(Xe0TI}SFHmd@(3GW6ISnScjY&nDT)=paH2~k13{;)WeO!uzCrUr&4 z8gs{hWs6`(@pENyN3S0WFnr>>!njDlBpXIO9t;StEBXka663RrS6%e9n>1d+VbH8d+Tp~ z`%b#yV6e=7E)_B1%T4UQ01L8gR7;*ybXA;9I7fW^ddTV)Z-}gAuusW0gbjzW zgrWi5%%Yi9>{+atW-7&bi*Da=_@X!P1N8dd(>eum~Z`E3C?bE2U z_z2FDqz8ej5e(R}1DVhY<(kdtOKk|W+%HaCb;ZKPSKug5VJL=3ruV*i-I7BDRC?>1+8EQy&X4cmO0yQ$m5iO z!dO00UY3<3)0(a~rovvnOj+2OzEPVYqVK}qpqroRd&{#svbVhbk_EIJrY5y}I?1A; z4oGAuEp@BY)&mx?pCMT_SGZmqiCOqV(O@`m;)N84>|86)0c?UeH&~cq0?-Px1c$|* zm4hPEw{Ql*C&Y#(j>8mljyz1%Szl2?Rl@l@>#=OmeD)9TIrfu$dt+O>HnX-kAR<3H z7uIzRLaf)J&t#NKKG*UfskcE)`9i%%YXw%@K_WQJj@(eAEU*%G~y=g5pKc#*YZiC1N% zhY-ZGX9w0!e|V-jpWS-fTjusywv^#d@VP+?X_K#%HX9GQgV%xrLJLgb>`=o_sPg>u z>dZzP^U&AKU7tJX_2=VcW_os8+9yzw?BMcJM#O@hU`8<~!WP42S_Yk7apcms&V1sx zu1$8Ceoxz*Bd9SUXiXE0bc}F(;0()8;z=f*<@Q2CnGh?(Vwu^0>GV6^efc9ld~S7( zjqRuA=jT>W9$#C2cIcg$-%)%29dG-{ue>#EoBnXQ-eQd^Xa#SPC*`sBELe?9&vBo9 z!K?kX)qq8Jw}7RJRXs^k2>%%|0=8miA$fnmsuz}2h7dt=4KkLGScT&|*u+jmM0Wre zYFd3PYY`VQBBHvFOo+d4&4w_4S+Z0T7TCiemi5h@|M~d$zxCY0)aCxL1>e+oJs@RE zCXzGmX$A!FGTjw+rt$E|D{6zQ-gd#IS1C}cteqOI;5z%QHbWxfLC)D0(|MaTu8KLk z11zTP{vcyZ#v~wEhk{1lbNAW1zw@}CZ^Kp3XN5Df&h57L#es>DAT6PkOO>aNMl#!v z2-NNOak$COUUS*btFNDt3V?-8V<|kOQpx@{W>7um($AFw@IaN;xxzZB+fZmSz7L7E z5EusT)f49Pe{lbap9E9!-eMTVQ$)2)(;=(G8dxi2ZenVfmc_OyY=G(dOP$B2_YVHf zKm4^F``OCQ)aPo0!HON*WEA5TJAaJ_QH3V(rQ*==a8(p>7Oad3D>fHgZa(zOS2L^l z%)vvajvOjZEuT1fbm7409dF-v_4SwD`hhFv_TwAT!HouKE%4aSrDAouHa(?+EwZ+~ zpsMZ`d~Y>iL5_+z)aTq-4>S<(wb`5|&ZpdH3*s?gKFqWY#He5@LLW)T3^{xVUBDDc zk1%4`DYeltm!unkDnq2AbKUu+2qdUd`&!XN0j{xV=3n~Uy?JN8IkmS)Bi!biNsINR z+Q8LL^ddYPl%tb_;(As7+&3a5+dQBTiE3FcSQpP|uNjO0&1U~l} zIw7kFo3-%0zi`7gd~I-UPhdNSV%~wPALZb^$Yw76+$pf$j^_#@se~qG?;7?7OmNSX z*PQIOcYfvR$L>EnRa}Y^LDv{`R)et4HqIoC`)ovlD5kk8wJ!sjF`ank=&yhB16N(I zaH2`gbk;hpc6_#brrDgL@A#(1!V){nWM5BN^fs}n@>7Cs&{W`^jOTCuKz#8P?>>9% zvSZIKo8i3A4$q1C?N3P_lIBaFPrhWlF>%}=@qAg+9 z5VadCS9lE#pq%XsX&DZxZ?4&p1&YaH2qF7}Z~Wx&ll^wE&nu=F!0>f1Flz~I@cu$V zCIiYqLrPBJ8}lM!aBaGO?F|TjylfcZ<^Ue>Ly5NlFcyy0l1jEb0@bQCoMAJrvu9Uk zXLc5Yh^R(v5%=9MJ$}z!hpt-qYxQ!0MUCk&XF*1Wn>%W8K12kSfQ6M_*`rI^NpbC8 zJz)32X0u49hcmOmJ8!ES>ldS#6Ea)yW{O~6TNOn=UO95{GE()une8?o+grw8Dx5v! zh8N#cUE;dEM4{G;v^k6a^6>@%H*VXM2l>DFrS`^`d)KDx8&%-JDz~4K=eVXD_1(CC zqiE+2Hsw!l{L)kW;Q+~LcsVo*mcz@M5 zR0w%|R@#g;7;_2-qLm0Ma-+5D(&nxUw_W|t9ivs_<3n0bVPQjTU>b_$boClIXu5+8 z_s5D%0#E5qKaFUr^Q)L%1Hgi~P>U+5&G>^lgD@=WsXtR)nh*jqa4^wTtC|-S!h|LJ zi%c^19>l6pS|N$`G#0tAG0}iLfv=G!ebK8W@7grXmI*S2)6*ym>&eW@VmBrlV-kGf zkIww?%O{%U{#LZB-}UPWF{C=ff=x&Uen?FCrX(Fu3co9s|3srkD_I+!97WH3^b_xi z+QelXqQK)=tR1J7U3zP@cBODE$D>JQa#6u-G2Mq8B|LW;) zfBO0T$!%e84<7p%Bk;n34!DDY(26<3PO7C>3s_?v4|BYAqA*O?y328+87E~gKV%w5 zf9rQX+`Pb+A_hh!9Ey^9sVL6tp|(bab#8)`qNd8rMg&|Or*)pAZ5VeMDRQZ|qND(q zV$d4&6Yj3)U%@dyZGWI+a6to!ES2YSwOvI|a0+>mF~g$iIXQd$_W!~k9SybQ9&K&iW41y3(8_$h%-&BPO3;n5 z4nm4j9FTOU2q}xF-apP?H}G}|8_Hkzk@P$|JFEQg(_|?Eyj)~8OjfOWE$>B$3OIMd76+@OhL5BAm=Un;ReB56xO{5Y)@|)b#4$MCzZ|eo6V;|MYg# z==2L_8okC$m==RLu2YIo24Gp0h$Dapw7QcTAF)VX-j)g;p)gEMqR=r|(szjV%l2Q$ z|ICjkUWs5(H-Dvualj|2>)xTQ}Sd<>Ryr2&H zDQmW63ds&?Qpq#i32A75%iFf1qKgV1I)zfs-JQo)luBe`q5gw z7@$2h39oRTu z{n@pn$=>Y;g1m0~IrBu-><^%$3eL{XFk{GIDo0y()FB5A%fy(V%quIy=We=X=QTIt z5+hcj02ZpKennLZuTAbLrllq@bgl`MJwLfc6rPA!z_zD&S`w8pA(}zQ-1Vi$4jnw{jdo47S*hT& zy(F$zghH3SY=kwv>iW2=SoEoSNjNOS?$y^`a_JS7g~+Z(Bz%@_Fh)x@Bca1H_h^g6 zNQ5V+rhDOrKwDNz5Mu-z_SXCXB46~_L&HD%_uoGH)M(d^OO}_G7p58#6exm%ZK;!k zo7B3q#&UdxIE3?v*lN85YcY|ZalG#zz3|ex4}Ij8_FlXC+GATVoOHxXw&GRkk)c$9 z(sY=a)JzkhmK7J!`pjGARe`xfH}T%emxOu|22fWvXv{A`t{zeT))Get&T#Kjeg>AKU!#K4w$#mL#TU&jq_7+!OtGo@v)->HYVnLt z3Tag~Sf&~eI7z=$4J~%iw~<6_e-pf_3+vA6Z7Oh;)I#<_O9(Jg?WLR5WV#yyDqCJ- z3rCLJQy`*rVqe=~>tK_VuxrLDRK$7AVJbsyJa{Xn$?maLj5 zhXzY02@rU}(+x25T4sP6G$IL%gEN_)uZXM+N$Zq(t^fWE-1F)djXJAw&#!q!37AZD z+;m9|XGyft9#vsjK+@7c;ND|&c}vbhmM%R~1TKm5+q?dF~++RkEk z)(#WD6t2JtEYC0&X+YCIvQW`9%<^Pscznm++B@I#){vb-(7TA61yji>hFj^h6)V$A z$$^$;T_HL~3coV!v(krB1BmG$5AvoTM95a3`;+^Yj@MeXZ7lF=;XSKy46)8iDB^x4 zp2f)&6&Q(1K4@iM8#1z?W!x*nBVOa|FMj;?>u-()4pHhwNb+$~d-)O$aTn+9_qY*s8_9N84Y<0@WA-NNt7_W;qa&)16+`&0 z&`OU<^w2ar^n_ew!sitxBp|sK*}KP2>Fq!yr}(R+BXN$3qgLZ(T+yr68G{=vkl033 zZhFSHGOS{CReQ!lEl-0x6#o1|s^{|*)+4RZvzc20jUVI%7O5@W^#T z6aY`aM4ODTxp$rqdZSwCf8(pmk34W@*N&T3jv^Up2Ps5908f}_D@*2a4S{_%A10;0 zV#;R^TjY~9>ke7dy zoUxMwMj8fkX&W|QF%qdde(k@1o`Ch@+FC+DNXu~RmqkAH3`?W)31wLsI`VN2tISp^0!QjcmpRyV7_~laGj%7?3)D}oXvHZ)%wjpUx^l&*kR*9NRh2YC)c9Pm#IYmvP$GIRiM8F{+ zu*c&5@x!m+PgOX3Bblh%P;$+pT18tBsB-caed`w!Irf)2n6+W?mq#?OuRF+uH2$`N zuuR}p33+zCsL(+7RGDx9XYwMALxQ2I{8c8pi;o)7Hob7hin}@x&3S~8Opj0yYz(aC zBftKj-)>G79kX=OeC|(wXQ6mhC2nS+CQ-CWFoGo?;f|(ocw`c07SGqyHH!{aQ z{u>`*U2Qt-*%)n9Z39Fq?8YX4ZouHxq7Vs^NR$>qz$V)eV-2L2EGlZ@$@0Yd@yphJ3|p4+9i03>DQHkTV=%dfS``N!WhfADX< zcjREPbMETSN)eRx>6w|%@=6?G7qDEyD`66_pk!`=u~~82^ASE2MUvo5%S`uDYEE2r z{kC8IpDo$< zR$Yq+C3Dc$BDrmKN~X?dr9yFmDoB5$##W%vJ&@OG!Bj{^vv*bBmQs*elgIX1PS~Py z5Bb=N2y{40U35*A(%L)GL>*cJ6t&|{21}d0V3E_7q`XCfv+xtr+Uy_|?W@LRtpxF` znRollpFMZ_h~EtMET8Q)nARb@0yv=;2psIgA)Ad1wTo)mn2yd4&%W!%{WraLk3#v? z*rSAZA*WMjy@4{SoyVn8t=5ySu3Op0P-{n*3QL8yq9cE*dT5lQWy!Xp*f^W2&#veB zbNG8c7ry2NpiQcGR>*CSMIf=Hz4T%Ntv{k>fl>64Fa+Gtbhh;@bKqh}IE+?p7N_>L zljhDpzpw1Ggs?%ltI*VlB2PGGd@Z$?9|m&k7lCZen5G^8x8OsJNE|NTaLa*fZrs63 zFoJ3RP@=MUS3IY23Rwew6jY(Gi4nxzk!;RcPRM@0Y_A?MhaWS)`+t6K_0)V))(0y= zyFP{2#_9^2ae*eb3R*u1f1_?huqoLvnTV1ac(pYpy*vNq-@Il2)v7sK zFOd8Hv-cj*lHK=x-<`QLw@jbc-}a8hHk3st00Ll#NJ11yiKHY-vYc44EFa}aIVZBu zv6aM0&T;IM*m2^>QbbFl*+fwQ34jEM-pgVayJ*|6?d`sOefr$FbEo9<{m*-g1)BvO zOIX;sSj?Mp%fI~ozyGg(e<}b(c&&DG4&;czAkY}o4-KP9ywC;Q7psI&T3Iq+B8sZD z6b%iShiavXZ}SJT+!3yZOse@BNT&K;5*b1+-O)m#C2$1w zi95W5`gQJqskT{Km&tX+{_Irc<>9Zh1*iJkr`Tf zn0ccf^1B^U70rl#8VwWH#~AXI!JT!98A^RA2}^5OQ>J(~Tz_hS`etrPuX%`CJNRra z<`Ra2F~to7*$B9klgO;-Hmsxjt?zyN-SvXhV6Rib6cIX3#P9JSgOrIE>{4kTO< zR%3o|?b`fZKYv3a17~XhwoX+$oeDeqQW0{TsVL5o+TWQ<6@B62xPjnOAVpyY+@?btNUneOC20>tb2d9M;A=4H# zJBbWJ#fwoRuyoxA_N>}OgiEC`?jguXwz5HY!~|v*pLmui1mT1xAxuOV_#N9}%Z&Rt zqCv2E^8WMR{>qCpZ@H7ho5qJm8`ZF00Z@%y(U+G9axtq;BqHXb%^ZeN3tBjeuus7^ zo9%KZv3S|8>@6SLb?t|oQn~JDV_qg+tyS-`eyBEb8Nhq^Ob$`r+USNJyOUwqjn#BVk?9-a#P7x@IFb+ zdW@6u8^OY2`S`E?(uc3OUOdM3a6OVXh(!PPp>=W5XEW)hCAy^!GmE?!63{2}1)sIG z$e$~^*3>huT3FKfwKR3P#&|G3=2q{?%ijXS`tbyMq2m|ctn`0*1b=8~e$q$WXN)K+ ziP0U9x3N-(1OkAJ*l!Z#YrXZF_1FW)+T{Y~%(R#5HoEu?6LBhnDD;bPOA5eYx#TL0 zAATw|yHLrF*nT>8ZvOP;yR$dkx*BAx!f2-3t#cuUB^ivU4+g?4-eyX=Mg$7SUGhO~ z?D)X>Cu&Qf6LV5tV*Z%*z_+F!|IX2~$DDk6GOV|-iBVR-b<=2|GWSxzix?cxOtsg; z1XWZOq7S2HZios_VXfJqIHu*+N5*=$d}8v>&yR9qZWK^_60_UsYz9rt5vzsBxr4Zk z=+zLX{<3r>8E9zR9Wc2NCn?5A@>&2vLVqrToYp@gQWuGoi<8{Jh)5DifJwxW&V;ZZ z6N^1?Byumap13XFabPUu98?%klN8X%m6}va=7o+!QDRD03H1ryj^QQ+{P?>$m=snJ zG(?OD(k*BPVyiM%ve|G?YFmZaYnTE8jMbtZgCnt5OH3e8WG!)$m2|Zl)sX>{8Sr*q z*}}TmuEmOoX#o^HmgqV-Ll^}+-e?h*L@Zt&x@w1g&r@uIm?)ssdM-k)5RTd%fY^Q1 zb_6f!_;`OhByW&I#)d;EGTg1LxIW;J@B&AKFZGxC3a+LzkomiVX zx(1q&Gcl7okTp4KeBbXQYMs2`+ zPk`(B`7`NUa%5F@dg-K}U;5my-TL8Ar6r^%L*zCylr&y_r?Xnq%l`~Fd3&ULjWD1` zG)zVA)K%F=Sl&P{(PkutN~H>wlf#I1%<{}Rqq>?_K+QyrHLc2$RVjB{^@#GE`~$Xx zrHH7a3#+Y}Bk)!#a0%k;GNi za1T|Nm){u#Zo-5VhYO>l9*Fqa96J^(A`v!l$p%z?_W-B$5Y||~Fhp#ZSQX{ak=(%; z#*<-3)0c8mk@f@frAY9S3`HA=_>lMy$rw_j>3YX-z<;_>Nc_MvQ}uZ_@2t*H^dTfZ zwC(5I2C5zuM&qRgi#`$df#Pc_J)(B=ZQDk`ck!(8RK8M~N@Y{9H{)d%8)xvP_Zmr= zSB=#VzZa`AJk8i*jdTqfIE;!fL6l@g$REuz;cysXxHzln5<1@Z3=5LeiZzokSPUW+ z3+xU^%6jt^>*+_2TFrG?e;mkAfnno&L)JC3z?I@!+Jca65L()eR=bePP_k{lKDBvQ z?uOe|JJik5cFQ3nG)f_rGI*%(PsjBKOk!zkfFW&ohh3krdnEP2PJtfmBumOxy^YFIGhSqO%0 zoDzPnn4jQ8AuUIKjv^_G*5Wzq#9RD3cmC|s(qaQx!8-o*WRm1Cd$4l0()DWU@RrWt~1*Id^tpe!4n0TdS5@l@d|HF>+ie zj12psCAeYZi7+$N9iL3CT06FS?a;bS!<#P4PHeEeLc)y~+MP0Nn>u`mPTHt!YUmD{ zF7>}1g+BRy39r>=(*buo1w1FBYDl-d}08EM$y{W_2zNe4$ z!(z%Ag3lqFDH#p%keLw2RX!<-0#_o;VTFJ^Hf~>Y`-eAD*f(m3XCWoK`4mxxMrdS& zXdDE^=q$fCo7qM0HT3YpOrOf@*hR&2m0%))D-JIvwnl3qikL5OYk?G0g&Z3HZl>#R z{M`3F!(tbLVX+ZO#t|PfUS|T-Z4vH0e|{*ec-i={MYUG^?t-J$idp7wTIXU{>}E*y zP_cl1Y-&uhaW38Rq1)GNl#`tOm3hUPNc^$MaYU%1{3*pMsDhit`%Gn3LcI{GSzM3b zbN=9q=MOwTU0v{oQ(N+>H6cREV%^2eBziULk9Oi+<{y+vb`U^0OhW$gql1WP?En zufiZtc8@M#Q|`p0Rxj_5VwhIS7!1E_O&#l>I=uMgeMeiBWVzI6)Y~wExRr|chfr5@ zUcL)U;8eaa2Lr90zg<3gsC?w*69^ekK74T7&XHR_boq53EYd|u3FuRvM-T}w z&f>j0m5>#07A+i#^_B!iQ&u41|3ryyJKl|uy5J>Nr(w;`SW7e3!}q>kT?C7+ytGJ( zbplUPcAOTE6#XG`T|^>G0^0aPs8sq%j@x^n8XI@O4%oKZDc8uP1`}RH8O-x=E za}vdNyMlWz3ECvA5D8i%1;wau#7Q7;aue57W^R?q#R3JE$aC~)iP!`bcSF6E1RP`b zwS(v%L>gL8e($aFOs|+8>jeFv6T-6`FVjYJMzDb-btVVhH#tplt6D1U-hRW{wGv%1 zM)X2!G`AM+EV~*fUwkYR!^K~jHkdE^ggHZqsPRBEA(KUlwrcl6(!tnTppK-MNEfmg z1lb;trd8rsO}|tQ2Iq&md#SYSuhtIsB_>&13#o?ABN z5VqNu5HQ#Wk(Jm^!zRMC%gze%2GiEQ7Z(m6oO4y%3{2@8>=OQ?s7M}i>N zplTgIV!in6k^TFQ;KA%w((x{tV%Rmv#l)b?4SA3-Eg%)1C`%ER95#RH5nu^Y=9VP` z3PvO7&4>N-032@FnfZsm`w4f9RPBnFOQ1bA8_g8L3=A2o;k!80T=J`UG;*m_J)EP> zcB{!sOHwynCdis~>bUjF{+ZVg9-ltlY|dr-VOE|nAW%@WZ8jB7a}iv5tf3EDO-EpJ z>JIFVj|eoKe!U(o04kdq=?tyzeg4<(*mFI)uGOql$S>xm1W_!;aY`0AM0St?A;qC)bn3;fo(<*qKA-aG4zdqSmD%kDG+CnewItb}r z!mresXvH4uQSd7EH8xQ0kPK6~{>Sec-F-bad65vs4vIrots#yxU|1`g*7D%~(8miy zZc3)eU`K;VJEIK!FzNGP!BVz3zP?%gNyPdLLw?%!Z~kQ z{Kl@>6ON)KaKKs^&3*K?tH(FVqr-N_LXo$dO_hrFRN!LWV)i5~)pelM`J>jGhdKvd zJoe_Rrxwl!Zg(_c6%+lTZr39M9mUD!qL>5mG0RY_EDpvdBQDEUDg_IJI3{kJ!1f>t zYK^%@uR5`z^BceOB`P&ha4C_Fnqd%k^IR_RLUwT(nAHOrVtAup#C&P+^1(NQ5J3b9 zco_lxZH&SR8_h*Ql&v{y_KbDtrP&`mu z@Dl)s(FfVxkPUnBHhUc!L#^D-=Y}EedZX`nYckpK#>}ZlPrQ-K<-YjqAIJ{pJIx@G z#T83jKEOdM%Xg)f7yb?w!-bD4Z|Dz9DpMa{(`m#P+PL^`)My&v<~enJ^RQT zb7xy~Q}uc|P*DP0w}j7lE+U-5GWrf`s4B2YETt9MWFO?nV!a6t1130^Y9Y!<03wDk z4!IkNs)4E@5k-DcCwKbP*+2fb$K!uos)gsNEi%0?5xkKK1%5Gp{^%X68&M*`FvltEh$5sV2HX zt=nrvu@>k<$cb_fC`wG$8VTPL3*os85noE!?qG64s)(nOb}r?NB3abhIoQbSFCY8f zUmi+jZoK^JcoJx%uwt@85oY7?E9{lcdgR)<`kWy7AG&w{Td!5);i%miNye~*C*bY< zfU;O!(2C*s_#(9{+HLeO>LCI5M(S1AqcTmyPkBJ?(i~*`q=zq)-GxsyvaL32_mz-8 zR$Q}0Nu^dcpIR3u8zGJ}1P&-)3e5ATFe#Xqwx0nbqYy%kGAKy~hl%Jm`?GWHsl&}~ ztoHDCAK!iTs*ip8svB<~N{#0+A@n*GK>=&TFQMZSWLR*M!SPobDXBjk`lBo;bevw` zP)Pa7hYl^B?|Vq>ZXpCjs@2T;g`nO>0pmo?A_5aDTpF4-GL(nPVtMUY0XN^~4O=&D zu&kN|!*QwF3SVejV1#0lXjHA~sqXZdrRSf1b$&XSox%_1Ci~-FV#G^eJ@v3lM}Vj{ zYDt#7FmItbImjgi(IN2>S_`X^F^hK*HB_l~gALW{)w?Y0>h*W5z57cOBwp0(OBpYf zDdzCC?hD-g*g`*2mv`>8ayk-M8991!uywN3C`?Y|xT0pUboX z@j)sP{=Wm4W5f||SfndLoEdlpULm)23WXt}@H$b1xsRfJZMsgJhBsOC5-&b+dUDnA z4O_OQM+r3P3PT&oyi1yt&kjU+Kv?H9KREx5FF$+Y_3Bt|yPur2<3pVm>`c)uHVC?i z?eG%-AtR-8$tgqVO3$MrFGJYIforD_(n^bBuM8^{Gr{7}AQ-AW8N|X3F{NYvXt#{Gj7*8sN3ej{>B0lg zygonoSg-fNJMJ8@v*~VBvH`0!cF!Mk@Av2{XlR-0-=hZn&|?klE`O(CHpgJR)y{$9 z$D2X3;0Y-PKC1xtYe?bYo$rSuJpA!Z$0^@Ga^qZL& z8U28+&08-&Re$vUH;){ub((41rX(%KR1O0L(6p1gz~g!i+`zY|s?=0Oq6FKO4)O{k{L?Qy^ck{5WpFPQ^R6-}pK0W3zuvWlmXKpOuDR?5?bGuG^HsjwM!hD1`|7> z4Z*T0LMOZa6XLy_X(FdGkV-?@xd ze*K&KdkIQd7dorqzVP8Eym&3lieVS!G|EiiqH8oOF43bY3WC>z&Rng&5cX!S zxnJv%#};0tcSjH?6La~&rQV(?q$Ox>ucqfT>FY?mW*kbp0bzmClc=< zrf4}ZBh?Ba9<~z@;qPUVSi$J9hn5G@K&wU!D(ifi|C)A5%u^T$%N>rChrgdaW35T6 z(jzgqumk3cewpW)en`~SfzBIXOShbKDnqmgqv$8RMm=C|NmIwP{N(p*U;pDr&z_)e z$oAF6(M|)mEJjD8G>M5b&@$m66(au7JhlrN`0?Koj}b+=Vk*K^NU`rEkopKpM5GXZ zz)D7^Vhzg|$pJ6XAJ~2`V|Q~_JHP*hsqNRUB`+2HM;J*uoi!>JRr{>3f8{B=JDznm zROW3zK7@jXQBRh2*&Q%WNO^`ki$O4TqBvTMz=aymDFfPuX>$d`V#Wc>6uCRJ91D}8 zAGnnX^b-^0BX@#r!S0zg28BIMY}ar^UKK!ZUbTfW5rc1-KE8+ccpj5u%3>;)3&sf( zr0-7L^)r5{G*=v-=-H*2nHh>uWGH3l<4&pah)e3Y)EL%(oI_nSe41mnijl zt+yJvLWZ>zc3Sz-bakoZWvXBN;$5Hn;yOF6%wvCui%ASJ_zQ#H4tSK_W6=&{QvoZy@QLFhr=W?fw{cNQ0#Vs#{v9sBg(yy_(5nLSp=zb*#`y zD&4kX{m^sMvAJW`eSdNIi3g807KRd0uD0k77yY=6)xQOE6f0mJwN3Dkn_?*jkkM6$ zXap<|2@TYj1;LfcQeb4W5h8J6d&UuhBSN9@w8F@|w4e9P8eE$IcifAOFm`bNo1d<% zl~+)l2h*Jk<8u?~(o%DF`kZY|A`f=(CKw;AGJrG$@GGYWq!4y-#sV7A&a%vvpa!UB zu+^aPF^>bHmzhEgR8f9<5Mq~vbN_Zt^N|r{5HYW!67@un z5OuKK>Q$}49+t$s(1eR8@Ixg9bORTM!)G$7dV~1++412vF|^E0JFEaGugl&hKZfT$ zJ}xze^=^j)6d0I7ywRwohjBq$2VaU_eClMcS+L{8F%Z50m53an7BMR0L^RLfssVit zp4g|1WrWi`5J3Rej4+Q>Z{zqClTuL{bS`6c=7CUHpjsH*R{Ju<1J4lE@gU3-W(mPN z4bzb!T)(7pshPzYI7QH7{Q(7dc4+%xt3V<+Ey zrRK+171NUu_+Vj3l_2fbP^CJHj-t?cJZa>Sxezh-E7F@1T{orRau_RCo^&DKs)zEw z$C9;jKpbbvOGlwxYigeQZr(8B{KmZqDR6QN<@WKzXKwiL78$decl6V|;8yF3c3(;m_2OoJuEOjgGv(Od$d(StXm^*Ey`>Q%F7r8-tpz)_M z&$<9Y&;*(rO1y$?EgywKL80H207KeNBBWqQj-VCA9YgRTfJLLDDU>m4UR? zNBCkJ?5W)p002M$Nklvt2{geHAi=x|vp0(r!^9zVt0E97AMBdB5uO&8^ChjRo0p%oNr+dxB z)X6zaw{qb!fJi%xsTL)##6NP~3Oa=_^1I|DwS#V}iDczAK#0As@zsC#cx};%ch`7U zA*f*7FQ=1v1RS`c8E9BIPv_Byf(!-WR7&lHf1_141NMwA=u*UVhOI`U8Q4H(yIBH8 z+lr9rxGmi(frlS?~pTJ3O8bSTQe9lW(NlMD$N#eD93C|?*MSnDvW&nAWl>z3W zI6GQF{F&xm*AwziY@Lb>63w;D`#4XVmS~QDD~Cu1I_EO;jd^8fCGqzXWLWQZTolHcpjboOdi~|- z%~xhSjm=(mJPfcaiCc*VwNpSAY+9Pz8eHkkdcdN4*q9yPwbt+-6>IuG$l00n5+G$PO%>7_bG=!GX6rP|J&t)>W2tFPg1#*3l z;;fl|OA$yXU$y?-|M|$VLlrw**N=}z{bVQX*j@Bq?0W9vEU8FN4D?iSMlE;=MKRQw zkOGOZWDs_j!qz+?X(^&JeS8Zz+_OYWo;fjFnw?IMULI@b8s&Z=?_radgk`KJQnxkC zYB@$El(u9nwJ=*=7HW77LyNYnm5H3lWm2ED&|+4jN!d5Wf03b-?7EdHRQtuRfBEV9 zY_X79M`RNPX#J!gw0pHiEjvz7I94@@!$#5+w=#E)3S%yDw)JMI8C0WQi-HWfe43oQ zQTz|RM0KgUaQ-}ni*l(UJQ7AaH&TR=BFpe{v-S_d zUorkUIqS9$U-z|B&(AI$&-kmT5(&#EOVqT7d7>T!!Y7-bCP?E0yU>g>MEt7FM$dxM zG!8s>@J~M*UZ(G0WJaBo^-92d@qh>z?yq1H5i%SfH5Ea7`Bu7}zBh*?GILYeC3I6> z;f$bHB(CwL6y4sGN+qR{6+efx%hTzGaMiAv3%vkWwOXzt2Fsx<% zG8jFkT!(S{DwYr6IU+^x|I6ok&0Hoq7N_p2bQ88T(1jRt&8XSc%{FE@l)2u_w7GLf zL6WRAmJm|FAm+$HQSgOg%w#JVl^b#{TuQPsGPmS*enQ;{2^1WtCuAA2jO1;D+%N!g zK#jlMvRebUkZ4#=wd2h@x#pg$CqMEtAHCul>8u>Bb`uz6#DA2*WGvkXt6*Q@FQd`) zZ-rL)J0L_hk zP_eI!p@b|B-lbBBZWu(3u9qI}&RXC3>amCKJr zYRqXyW;NA?#NN;ac#R>(EIG^J-K{R@XeV9{+QscR=&W-<(bEPgN@v2M); z9souX0Wl=;oA$`y00k<75AlkB?4t<2Zk&)mS?4*$+-tBv1_R1>ItG{PEAGrI*7{DA zVwlrM<>d&t(>2Rh#@)yS_BzD4};NPVFFsM(9rVziKgQrV52az zXnY*}3TGl02j&CQV?%;;267t0o6!+DL(qRQ$_TP-z#D;=;a(^1Mo0qWln`Z5>)MrG zf8I~m*KF}W_UUVH{m5#+z}+OUp~`u~f;C{7P$?E=Hw~s%m4bR6R(A$PE;pO(TV$<&xw*nNe<(! z*t$;;;wM@mS2zxo(zdqWkeXbx>kt3U_nW=M z(9l?Ep*-Y`$c!YOKx^oTTFil?gUBNc(oN}ZI*?&8e=E{l4W=(rH;pWb(~5L&;y>|A zrow6KzOS8p^gFKyOCuS3ZEc~GaSEM~RTd59M%h%N7O{4;8S#(_Cij=pQyuaivBY%j z#nFwO-PdjX;D@f+afRgr!!ItN9Y#i=zO~J=_FOZ0-#1c!`IY@ki}f|*I}xB64t5L; z_cFJF<51DJP3Q^%aNq=i$Ve_>x!NqGoQOCph9cfc`u#*U_1pjSiU+>6{<$aKJpAfu zit=`BN;E?S5J;kp=b-1O>=A+zRA@^(r_iid#49lZjod(^XdFJhJ~$NVGR&4KVP&)I8(E++!tNT`Rc!3;hcRh(?#f0YMB*ws#dbO zaof=LD=)wD#x<8;MO43T>G&{Z__*#R0@2J~D#sKNLkQazu~&^zD)StCK+D(1&udm{Wd|NOh1g;m4;&gv5MPl*E@ zi1_qSoN*`Np;K%R1ueR0HR0fI`Enh*(=qpT3`ErUwiyJZ@IlqQI|SwSjy$`5Eu?}3l;Oo+m>G| zU`&Y8^0N5=q^VdviYkrz`KzyA{onlFC$ke)mdGyx8hDVIOl(K0TS~@EyPug%UVBUa zq3<`sreCg}AIc76wql-{Vx24rkwoAG#U=47lorpg+fd+3g37YRtWqeHG7O_^fvuPx z!C>Wm;`4<&fBr@&@ccQev>?uOe)c#n0UU+>a1^A)m!7mvLNt8BTV8WL`4)%H$RjXvtnCJ#gLsdSqlKAI8m!?I8r&WX>uPj za+uRW*aXkxxtGduNir-ZMD)-9P~7tF0f`T|75-*3H^>)S%0xAi_3@o=EF3#jN_Iy& z-6VJhCVhkpwXy0Z`h!%#4Q(tk5-JdkVoV#Mq-^!T&O}V@%AABRh8QCA!MOF+j)^ zz$9$Zip?aHr_)wFAFXk^d!Yfe8S-HiBor84uuL(0!a!AH^>kyyBgLf!N|_UmA8%AU z`P|5vSFAt#gC{#nldVdTwd?_%MZbhUVFOj8(PEVl70LeZbQ1AivxE9s%8w;dZhhU_ z(a(JTnypvZ;MrMJZe?^;VT?tfFg9AlAk|x~TxKmX;l+_5?pAL$oPJIYIG|==Te1$b zH)!#+qc$L12%4bW6-Tnm^PpYcV#8nxm_$SxHjZ|?LCI_O{e+bx0^`sA;P8QG%8BmA z>Vi8_+z8CMippxPV3@Q6jY|W9WSdE)7OM;8)?$9t@lt^DO#jla-1Kw5JlW4$$!wQ8 z$S^0S+@Q|DV_@20xja8IG{!pgv)0f^x_mzFC(|^Vf#wm6w-^DFs=o@9jXI4?)-6}3 zhexmQO!Y{c+OfEjXCpr0ar;5QOxU+%FQyx+Ck|&;0Vz9iv`QFTwQSIQ2q)05T&lqze9!g#YCdX{RX23G#4nqmkGgPd*hkpIxfg?A7}oN` zE}1{{;L%bQ$~(}PgTWPx zAtjk?o52J1Y7VtctoFhIjX_-`5k=iF=OZu({)u&4Tde2P!*4tle)~`Nop~c+x5m8w zL|EshRLxLBkt?;pG*nnX$WX&?&E^Y+{Oh|YYyk#@GDwsnj6aN( zMgKGr@M+gh<%-4XOgHD+Gt>1p=ImsF*lsmXgSI?ke9~Hg^tEj;KEZ~K8zne0b!-%J zUodycP88r@gL_7;8X;G z>L5Xw0l6#{o?ALNlnegZzq)(VF3TQPL(qGG(LhO*h>8*qq6Y@AUNkf^T3)CXvOZ$+ zxii&XYZOUR82sIp~jZ0jxy)OLVs^lv}~2A_t@E-)I?R`pkFM0O0l>fjYZVb0Aby$nbP+9Qw_ zfK^%!KWx;3_ehGO~8v4fr) z&6JigDx3^M$!CTz;w~=E7qbOyCAnl_>7?~1|JM%=ztkAd+|;**Z7W4qZI{UJR96BOJN0wIEpqSae!C+0tS_wG;r!kV1XRf&3#GN|!k3K%Qmba3ReR+W2fcM~bc zTAZT7<1yJnQdtti$#|k=QjE2jX(3PH2I%7K3{|3_wrpQ3(K3#yDG(lo>uPGXT%^q4 zxOChRs}_6c-sx|D<%M{2TftvX8TU-uSz4UVWhref$2z){WFT!kgj0&`hyCXGsK3xS zIlig=kN@Y-th-X%Q@M6Fl}nrT$t-I-<0_uTEc@g_lOu>(u5KNDBPcHhMK2BVFqCE1 zJMCeEM5Hu2IPXZ#1jMwnI6UExj!VXAhmj|r40Zt8BLz)+py~|Kz-~Hc2Thn(3iMC` zu3|ArCugUvM<02j9s0xh0(tJO7JC84asd#_IFGR14kd~bG3W{is~rytA?_B&d7sIp6WxV9_DYPDD+C&B?gHE&6u7fwyi<4C|Z`sw{P;A|f%$9AF+jp#A zvmw86r{?`ffokJQku>Agq#9 zDD_pfA-hbIOA3s80mDz@e*OHDz1PQDc%awrWOTBFr7x9IEI`T`_7-A zD9wa^ED!OM#bXgC!SHf6$6x_r#iulFAKa>??090`=Aujfcnm)}cz}XF&sa}9xPL4^ zfs3r&1zgM{%9X*D4%|;L&JyHg^^D?P7kI53uh~ZAx0xlT1L4kSUJ4)6cBvmEV9GGq zv%-2%O948BiLjmMw}zg6@_4-*jSg?E*Kjy+qMP!w5t*ZsYUq}>1kDYJ>B;U=eJ+Sk z?Yzp_wO4RD{Ukvs(6G3NjkGGqtNxkQF<2ms44A9fK*e-)VYcIY!=!S|%`GH6pM*Wb0E`@rkiWb=wZnE3ZO({O z7$fm?{f;j8laksr25TXJ!&+yke|;5-e;RGn9Y@t?zzg_PHON%Eq^N-BAM7gL=y)aZicR1eLkyo)R9pof%I9 z4+VP4LOY!P*}Jd*(%;(xcw42mK$Q(aLMXiua+ht+aC0Afz?*W@*0cBb_CIr~ z-^#`!7w*>T2jCD07Qwkvh1%O_ zeAHsFtfL3I_kQiA+I(sxv#K4-A_xL~$(h3=g-VIl2yh_c2uYOU-`HwM5@=bA?cn&x z`o@3zkA4AeMNnufRv(=lZMG_CSXzRVM3R!u(1PWNhXd}4)z6tZY(4N7Z=5~Wa{J>1 z+mZ~eenN7ZdZ~QtK3up4~#&*4z23QRs@yJT~LoGt9wUwv@i8dc1wV5pT&ICDe zl~m5Behz_%0u|9(X7w8ZLHve;snXP%Bi?4CB^1jGUAH?tky?M*FcFEudwqz+lI1>@p}^?C^x24rZ4dVb~&H1RsrPOX4`(gtcvK3^Yp=F?ifEVVPZy zA6P|vZyIAr5WWSOIxcxV0TF#f{j?j_iC3)ee)Gj-v@UJ0DJ=wRR~P5z<}#{HK-RZl zMJ>AtkTH-95F*|-GVf(CP6W4oXxm+1*n)cqrkBf)G}<*%k;1T*aq=Q;v?x%^0V2_w z$EM-*n%1e;tZ)3ulTo#3wMYymIvVI6zoW!#=ol@CL#u~BtVShZ7u~C`-IZ7JBL5U6 z5D^)H^DbgeGT@r@+KJgC)`Q=E?af!JGV+fAN^0iG_sn&1^`;qj196TQ%Pz%f}7&h7-(W02-K3+M)qF;e5ZT9 zcJ%d^ma_Kcz(6}awkzZwDtaSlXr#5NsV~c)v4n2lv1&6`NDz-ZO)#D*)8T+zs7ZlT zSl;+9c>~V^DG;Dea3;VhB*Qr0Yx{5PUzk1Ry6s%8-A<>5l(W?X0~-YuLcuG|28JMv z(}-9H*CJ32Su*Nfz#A97UML(I+#>^qQLU+VnBu2=FfJTbEW-RdL1kEF-8R8tgH($KhFP9p_8rY8*AgE0* zTC)fTTCp%MisizvfL`h*dVY1uI`H(FSU6nFY&d%Ct+ne$gCML_f{BSjwc5r3$dx#- zOwbBfGt;PHK{DGOUY*Y8y-K+?Kf|&T(l;QwY}|U52$6pnZ8S4{k-}2bYc;El>}YGt z?lpIQHZ^fMr%+W@##thQ*q^BufJhSBnNiDEw{`gIQ(l&1PRsxJvEO`ZIll zp<>IBIx;wTu6e3H@uj*v_yLOqA7Q}k{V8m;o$k^?9e@1no7Nxw%lpHnoY$YUI@r+c z>6zJ~p&Zcf?CR;X(D+FBgLO+fQN$MTd(;OC$PN0tzjbG7gcL^t&6I)Tq_VAE*>_Di zDUZ>*f^!N7CeZ~jD|z~u^)G(^+tstVygO`nh2m7MmhyS(C12GuK?Bv9nd5J`;7j!Ah9(UX1em-A&{?XG;cMX_)`P>)^Z>!ZP5bJ3J z2Ud|{%Yu|XgoJ~0g0jf9o;BZ#P2c@XA0Az6r6;Uv=MVw0z9Dd0J&@rCGzFCytI-O> zgauGOgNQE=fCxAbq6!FkZg&Xek8l0qp}C{6VRy6LEs*L*&T7mnkYPbJ6#`6~En#oL zAQl%E$mNd6)@{z_$GY|2(ck{ZcNaET&QL%awt~{Aeu11qp#-u&+BML6rM?lm)y??v zV&iP`xqE~E?pOb$ac(3N+Zt~V+c7H2(5PGm&eQQCl2Up=0hbUN;K{^wQKK*Dp1kAZ zTQ-w>#B_qiW2fs4s`&YlMX|30QCCPdM-Ev3;SZkAB`!}yL^Qc5<^ss@3hh=hXc*;` zHQjL#pXG{8V%Uzrs0#Y0e)bDDf9CUR-Mj$QsT|g`n!SEAPR-|jBOpxPsl>cWr(f!| zgLpI1p7oZFTL0wlKm5{z4ZpKBWv`*eEoP%m0_^D+c*Y19VjQ%|CU7^5bB)`WS$Ji~ z)k7b8}f@+f+Zpt$PplDqHm?J>uHh8OGqXtk*mOPr-C*p%sFa9?0bCf=u4+o zWmc0ukW2c_O3TN(E`by$ETPD_1;bn}Q!ZBsl1itGv!$s$AIRQzm(9MJ9VrSTgJk<2 zkvuNZfdD~GSAse=}usB_w-;)`Uj)VEt?5r};GND|! zd}v?NJP#UVUXk+H7YAROU-UBA$s}uOO^avoHAOb}4@dSwSU;FYt zr#IFrC)3_=t;W(d=5JX6m}=~B#F8p9Wn^SzZn5fS<6*GaPn6ee^ltswR?Ru*v%|

~L6?i5@(Ax+BvD(hai%WifOD8;q+&YYKmEE9}*C!||(cUIPnmQJqB8%p@WJ zi6!wkLl%Q|@iw8YP|IKY?~lI~ZTykr$DjElr~PewaS1XkMhE>xLJ<=UsheRSqJfAN z96E?Y4hEBb&n`ao$o?T`k6GInMv7~f2N&}^;k?FXWXkXoniK0Z+NHvp|nQFlG>Q?z+J(h!Iol{yo5TfcWH+Bm@v;15bqGYbNp!Knl*Xk3h`5M8Lg86 zSGv%MclE|l=TEr;9a+PmN2oyr{3J!mxHp~Oe;TIY^Q>Y;En_HqHL1|1R&T}(PCR;@CHvAtxvTelv6 zpnCr|_e~uQ3f`4gm`cQOcM@IzG#?zA#^6xkxnU6CI8W7T+=RTJSh;mFGg|+d&)v9j zX99+W>>+ePWg>DB1!xMbcAOBU$G(5+<>%knI&t$tK%s1wFf0}smvku2Abr4Sg~c*x zFCs3avLMyWIzu645I^$it2SIkE)g9@qq+^5f}l*8xHB#8K;e z-#YosgKsuVS=Y)WV;<#d;rimYvNjNTFC86)3z>y%a4P8%JP@;+XBS@k!r#5=mJf~6 zezeV0EKS{M6`3(eiW=v^p?P7XX>13XyU}j>I3~K*tFP2vefdnRzbcs&WTzfQ2gS7* zcC!--9~S5{oF|DePPLv>yKfx7?xqPLzVqL*+{A6U0t)<3&Z<`0|33VC2{J501TwiW zaj*kf%{ef-dthm|iDQY^&rw6+H77BWNmFpp{QSd1&z4SNx(y0D%@Fed06iEhPKZkPjt1`HBGN-L-XFk@*hI(2N}nvdi$ zPNFu-C`1KJ`IwLc26j-fmmhA+U*ofR>@pAHDNwu_1zh_HJZo+1@dsZyeRQs1Uyc$s zlq0?$Re?w`#pG*sX!OK!;R5w~Pi8DHS#M2^t_nW=i?>jQ3-AN73voBXcComq1}O3R z?XcO5GTtbjZ;Pe)rm!uT!hr-4&ob6mk6Bb zmm5=w{Pbsje(&C!2{~txQ^ySdhl30U2r^-=6ftu9@#BZA2kv=#ytuwYpg8q8Iz8f1 zML-zW60e8lOFRVihj1u6DhJc? z^z5eH**ovvo?cDmvH&n?FGjsm@_OJns!V8PUB*|f)HsO6@HIgj#N%3(8SA+xj&)i? z`RwXcoXTbhUy{h-r^d{UqgR$DO1d#P7_;!_B%HUx-6MzUBZX}kF-=^uIeGBHQr9dK zsjVe0sRJVxqInAo&U`W--f-)7W0kPhZB+TZGbc~@aTYx0R+IxdW4Hoa{Z0;bjk3mD-b zfl2Jh3q%qfup)p4hQlVwc36K1EKewU*>FxyWeG-#PK>8;r^#awRqH3N*)ehZr(Be& zUJS_aH2CsdfXva0g}qJ_fefw9DqG+B>dg1Q^K5lKo{Mjy)r&-@~RN`!91sWK-7*YH?~rJao^Y z`P1Dk6KhJ-?P87y17fBa0fbhKCL^N#fJqQU7p5O~uDa+In#rN^CqH*XdW{u?HR8)^ zRZ39@83fJ*8PQ-b_+)v9bJomh>w#~-_4tFYS;5G<;j4NVU1j)PQq8)AiK!QKfh(SzxO5&X)df& z+Fmqd_|&!0J;Oa^NGY6$1{mpluLqPSS%)fNZPo+_`4^?&U7K zMQisptM)y2q*D%zYebd;v)PL0Va5_!ss<`5mauefBDE5uXHPfIpSC864#kqGCyQWZ zpwQ_9o+ax1PjDzfM6#9Px~yIS=1M;ftw-;FefD&F$XU}Te%sD8fZwEPn4z?E$m11{ zlX#`E6Cx-HgKn&~dUN{5JGRCJb6+O*mb&xc&nbt6I5QigybI(Lm6QMY|9F)u6^k>O zlrs{K6?!3nPAEDVsvI-Ik~r08W0ygL!_le93UdUjcaHbn`Md7E>h51z4~~2*Y#m^?&oq#|vBx2wblD6ky$_%UqHGv+A9)94J6K|f+I;&Ftgx$#@ z3D%qKq|dpUF&sFp2*VV0wq==2=h8^uF&C@XVy!*1eecj+pWn6PdVy~=qN<-ufkTUt zYmhvpeLw=Yp$*hKtkmKzswqpPtOF`u!`lDs$wtZby9JUwgJ$5Tu^r;7l+eVf%1D+| zFF^rsk}+++pG?aeTO!Nw|=ARt!jLzL*!6ap6xn-W9j{DffAQ{B)@?EGCET zN}@LMi*zpY*-$Y{FNt&du?Ons-fE?j8^bEl;;JhABO@)Dowz4`k5B?B+SEY_;(mDj z&ATW_gYtst!H&N3%85;v4IO^2LAnGf`cRX8f~YZTA(&H1RE$gF1a%Ft@uZ#6^Jh*T zeSL0n(-=x6OGgVpkKqNnp(T7FsqU|N{4izmd2nab3V?09)*CNbFFy8`)0<58hXJQi zDMRR7d$)FcSO^rZx1w_vSEorCF~1bWD|dc;&))0vA#efs`h~*@298U$1&lZhVK_^D zz3ImvJbHLPMfE0!{k8aG+3*RbPb%EeiQ$Nc_nV!7WhB``QKE7%EWNwFTQ|s~Zt7-|03}UJ4ZAunxeGN3QRY{npFRmhSuJ zJ|{k$wl>5&IA@)x*`g-Qa9*WeapiJP6fQi15_LG@eGm%E!4jpZolIr@=H$=*;#D`? z;Z$3#>`0>BBESJjIt~~=NI?;pVwmM6pPNHwseuJk09CMcRQ1K}P!J{cQM#+ZQB(i*L`p!W?9HxNVm`r^Jfmt{t8K~{QWPBJ^ko9 zy5ty^X`&$oh?S|Oa?8ahWRzO zun?U++2~R&l{hwWfQE-nUXu8(09YLemeaPx;E&eS;`%2-&2cck2l8)>to0c zJ)dX>VXNZ|7u1rWbRwa(iJOsQZ;u739MHXUdv6?e=A)x9Y0`w z?=Sa1|JYllX?y+TO^dVbQn_Eu_!&&-INqdcqS+t-FrbV#JBo4)6?o48C98hEx!8R5 zLm%7ptN-8{ZvuTS@^j=2#!IDIHiw}a?L2@j29>(+kMDcF^XLNyCWp4QYVks9lz4Zd z!*luJNCEAJTS;ysQ;L<#0w+$P;pUo&boGW?cdSReN>lxc+@hB6dG%%;Hk!7Fn-%M& zCz|(t^VNM%o}C!l4i|zgVYY1a+G#JBPI;hdC1Wlo`8X&Wpx!tH@hZiJ(Wtc-{9;fG z&#YbR{`}wh(ABrbktFR*XQ5Im6pPGhqgjQikrE1|GQd~^QiDn~%L+-whRU#eaq_^M zI0ui;PqqDimiqNnb%w!-f;bpGX%1|Na4|$+oV{Hymx#Rb?rX0kbC9fK0%%l=PCCMX zqg`NIdSgP7b*KNrk4uhW4MYU;+}Xedi5L-G30^G$3ZMvM34VS0L}SFijKrH(y`99& zE1)F9Y9t!56rl={6G1)X@4!~%EBCu$WAT~~Y+buiR=c3pvZ?i^5O!U{$N zK>{gWD2P{la&WBdk80viI0iqUPSSDhsLO{EYcOC@Jz9(>mZ&UN^I3oY3;hT0eSS2% z#R>uZ1cS@>Q`Gg6*zm(t^{MZ@RX&$YL|ZcP^wLa+nw>bufUY8}K-PF^J|vMSDH(57TTG`%3j8`%corVIXf&?`@@OWb(dm_*nl;ZFd}!{4loJ4&F{ zOHWrG`qq(`o;YtchBvRev9wTQB#@VhZ-8s!rXXOSG|DIzu_bva0-NIC)01V1O6Xw6 zYfja}^V|0f{rW%n$kwZ^as#{#1*)dK0wNC@cshgn*-(vZOrdb84oR#62_mk|P6pD_ z8SqGq?D#?C-}5M;P7=iQ0Fk|1t>ltQnz`bt1OfAbqq-}z4esphxW_0lAiwSJ-4I1U(&4m*#K8H)*r^ltpu#BCO`b&uI zR48GUAmu3J^7R+GZ@s>lb610mLM10Sm``lg+Y**$l<6_<7&d3F?-Cl*1DiWq>e!Vn zJFm_R6OoN@ME)RUS~%HSy}r11Q{m9`BJcxNEKSW#bP>|6U{5Gb*vEjZGo~pK8-(G; zGwphC_)uqjTfz#J&8!45EdOx|?wBxX5y%SP5uu6rOskfu@STJ*kfHc)@T(*l+3#Tv zGV8NIQ920e=zDIyRkh9>FC$pl-F#fRd*qi9n+=CX=;00sLhRr?n{nW=TDB@s8l1o7 z_T4w%zKQP$@AN<|0k*hAs!C$tQ{_kQJ9O~5+1gCn>#nA7vEx%^8fgvohgBH=2KS{& z+!iBvryhbH8dAf7+V&ARx?#QDpI^PTd*|I(-1*sU%DO8{+>coGB+kMuXH5Q)C% z5ds1gqf%#&TC-UvPldCLA+FY^vMut z%`MJT(lwn=*{N0#HmLex=Vm_f={+~zaoNTlw7^QH+FmMEsy7Jb*C;Ub{RpMbOe#)E z(G`()-jkK$b9gg zslf(C93TkXKpKIof5kN$WO9Z)9Bi97(82kR)~rwNx_ZsNr=IOtxk$A$(X8Z=gm$ri zlA^4L9nvE(_ONP&LYbrd%JWYi|KLZrcgdag#Iic=DoG)n&oLm0L1Ul~k(wyS7?sqt zA32NyJ79500`$Z1YuGRbT>_~X0bA*Ej(6rQ>)`&=DtBxN36N+6L?3A!2n29-#4OZN z7z@jv=JQyFA|Cqb{?0vXhR4MEa>Wt#zh%|VCBFRC14mz5I(@i-Iex@j=MoVr9O*8W zV`8D`Dp@%uc6>nTgwY2!zKeLL8qPO^5`k;^VQ*xl{hPmg$LdX1q5x!jh&{}4GUQy7 zmqwFRsg&o@B$U(yrJ=3;FH-7^sH-v0&QQ2ev0RL*)+lWqm@MhF6l?W`n6G$izI=M` z2RGdH+3g)EfI%*uw<5PPZ9V@?{kg}EA9`V?-`X(bZtT>Pjcy41EF(?NO>_rr91|wn zjY1z9Evri9YDAlOcfNKyUFfIAdgbug&;RPRw|;CJK<01_fLL)O604HL&JQP*KTmAc zj4P%%#9eBH;Bam-z8>l_$wuTYM(Dfo!w0Pw9zD{fEM%@IQz$^PeNaP+aQ-$a2;3Bf z7c&|G%2qS#S8L6s%O+FTeQ+~)*|d?(j-s_lm1ZmkTzr{S2DEfB-K=~Ak*>&A1F~DW zg1_Qbj08Ow<2R=&` zXc~Jya;V`=E7xmXFcLBe^WZR+C|!B=2k_!y5=KJlfhGNDc+a<&;FdeD|J2=^$64xf)$t(o zofLK@6dM*QJ;B|slxB!X-oZEpb7b2PEif`X zTy`X%>_;@H=RRlfJZx5~5q;p}Smyf$&My*{VWIhEdkyJ5=H1>`;B zvxY4J!nm~kicM>`DlCT`nj5l3DY;*Iu|z@*jCs`%@n8}7@;921cOINM`10aQ-g%1o zBvS!m`}_E~^cWVT3b|TdNG(rf97wujRTr)A-+Qn+?~Y|wM=Z1sSOEg&fI&cF?>JUw z8csvhjgpRgjhNHE_NHAdq6lMyn{3IYjR_V97{qLC*rxb~LyuOwy`tl!fVhPL0EOKL zibhz$o@Af|7GOx4%wZ=P%Pr1MKl}KJU3)iiIb?{z1P3uJT6J-}my=!3%VMg~_fHodec)Vq-fz`cjTFbhFfT3E!=RE)=Yi@csEPu&3?obMgl@7hG?Qc=%*ABi#_< zfop+RgJPKSlbNVt%^$a3disQ)7)vD!jSAzX+LZvP1x>uNM#huq3pg!uJ@qZZuwP!k zBfa;7YuQ3TuuvNjXeDVaQ}a7%X284#gdv(394kb{xuTSnkNT1K0~^*m??Yd7=KK1% z)EE|9tfmKJ0v$OB?lql#n zQPE-(5SY`&P^I!;WS@u*M8;sUbhEGQJ3D)J(>O&KID}5X8)8+ty=jW!Q>G(&8IaV4 zj~BhsO$}Evr_p#_&g#^>VEWbm%Mc_J^Rw2mH_zFzEHDG&(_JiT+VKgjfIo3uOo@i} z;a~jFx%S$<M|BRX<{+E_#uC9HXY9AWBLi zz7!;xz&d<5s+79}yq1qlF1Pzmo4|Wr50D($og)b!9>Zv&D*Gy!%>hV z(e}8yT|0N~9c_8{yO^X_8awz4%ela9crgDs z5Sq+iI@p%3bmsKpu_III)CM@F)Devn_7<70r1`QxYmutm;#13G!pFqqc=rBp2G2e5 z*6CvvD;!I7d}4gU3c5s z4}EgyuIv3IIl2Uk^dcvfs^guA$2>oiih~{2YzDRekG(emvg=Ck`|jKK_j>O}cVpiH z0tB%WBuIkfKqO~0Ga6|!v7|^Y#fvOwajINNDXNrJNja%kmsyjF_g2f5I)$Nul4b}A5%Um08I2Pj#NIIe5p*1iC}RPx_>RBY z3FfNJlN;_Cc!ZGK~XXjpf?o80hw1E10GagBqTpd&ZRIA226_6{F`kfrv zRq{&&+RIiA-S^0N6vH2qZ;K)(#h5>gZy0wp1OEYR5=pq3dgOA~^^baYi(_FT@K@TR z`?1T8xA@5&ar!!&c;DUbR9v``bn->vVsVJHikX1{m`JhKsX15A6kmV-^k8&Ppqa!z z0%pLS>_bWDW%;MNFrz`*d`(oE(E!mdRZ;@%Ws}-C)Lg%L33)Z^DkMS-icG2mo|11D za}i>GtzO71P2II?$>gPTh2mVYP4RUs=xk8RzmVA|4+9bm$%!1LkPmp3C$jUwcV4^r z@DuDFtQ*WmJq_Rnj~R(ca+8*O9qQY{QyYRvDB~-T&7}eFnG9$nGX5)B zAwH`iXXa{oYN9Zn?!jcvVGK-6OXxv=5boG~&>Oyyg_u+!d_^srR_qV|)uG9$$y%YO zRiy;-K&4bqP%DOwu$g&s0fJI`oIoHJNu%0gd(nR+P@ywO8$QCCv%biT6Py08f8$GQ zwuA=P$gu#Yp=cBm`Lek}%;agpOk@hfR55##=CoF+D;F|0sk%Z15JM^j=hC@pzjEeb zkPiogq=V4T22cPxHLIxh0!o)k{1m9+PIwngUTpl%|NQ!i_pS~0tXZ>c-TbxrQmqjR zBfJocLy5CURN4E24a!F{v_xvDfC4=S<^*M+6951}07*naR3%%r10@nXP;c?zkdTiulIuVQ6ST-cR`81;|Fsc4P_Zh%m!OTPJBn*iFG922jHxKd~D?ci|_a?Z{b2_}b3sJe|BmVW8lG|})^>%MI7lFp9( zt5&SX_D-%0mI_I7R1MNvB~_Reh&4$WyV!ZqNQA7I4cbw&1I}9(!u#H5AIGe zF2bilihY+AVPo;4TxUerJNfV&u%Mbbpt8d5ps9H^Abmtm)mji-AQdv5wBT}ry z6Om5VdFqee98KI6Z4MKMPq`Pi4W+370T^xD7|#I{8fO(nTT>bFW-W-TON3qQMu7}a zW1FJe_9U2Q%3z6kSGOXqZx3TwtD^7KLs4x@?*H%l-uMOVG4;~i87d*aVXxp(j-h2I-sRLgb3iM&_$~wLb{##(Ei%H7!sZAelXXd|= zS&XhHjm-d=_z9g>G2SAJnG-w)?%Pm-N(Ee|T=LX8p>D#pzT`Odp#f1~k#(PSli!of zifPo5;mr_Gn{KEeke2{E7}qy@u@McfNrZxmhML z6nacOl~#c;0t069=ZlTYOV`B?e&wDA9^Hcu$W+I|g*lIzBQ?1lNyR0PRtYPw<%x5N zCzAY<{DOcH4q14m0Y|k$izJtqPCL2j8rij|l80wKdF*5_!_zAgL`x4xkuS8L2w9A* zi2Q{lNj{+diIb!l?OQQ0#s+3}zMhWvqsG?iCHax1Su7%Gf~8`foUY8~R=FPQ32^IR zgxbZGT={Y*lD+?d%?H2y#P&VpBGg&{j|GY8!TiQkQ%)Y^oTL{qATk=o!3f)dxa4`p zpb5ia9;c_WptIFD*GiF{=8E&uk7j=O^t)$IR70($g8>o=#xX!iWpvs&c@azwd}wgd z-hFjyQ;;~#i3D2#qR7v$7;_GOeYY|a=1wVzfngeppE3yJWkiD(BjLQOd{ zKLDo?uBB3wDa)uWNRWq(S4^!;7Y3wEYBM&c*oDMi=L^L1#YQ^aj}T3wvM6gRdsq$X zp=%%Djy}8vEElbyr}lqz2(UCU^(yNNW73`9<}7oMi3h(>W;9Jg%%by`|Nd&EIT)xX z1C$}sJmgF49*hcus%FeYb>WW@#)`y@r2ht|GWn81RI-+L^E5jR|+W;ed6@Rpnm;0>*Ch-Mg)Nv7lrjQ-%s zw+}q_SgX{Iq}k37k&8Tr2fS`C;m$02nhBcWI*dVt2j`zU>8_tNNtfKq01-j3IS?jQ za|xBfDLCaETi#}CQ$N?ix=f=yh@MthZ(3lM7@8oVKnQa9_9m7S4Ogp^5dos|AF^AO z=s!Xv@rP$(0G5PyBUL4P18k1g8uQb+%hcBxT^awGhj-lf$f}haNeJk)gjPiSsZpjH z_u~!ovPigGJQfZhVbz$cRCOgN3c)Z;I@U_DRVBv;jtI_`v$M04^JAG+zM@9jhzgpD z*qHdE=fL8Oq-Z#Vkui_N`V#}k*e7PiKy#MTYE`ZKxQTrO9L#$wTUgtC7~ zXNgo~YW^&xQ9GeR#ksm_YXC_=w!i%0FMeU)!^_D{DsszJM6!$SB4GnxRw|n|B>l1w zNp;0*LAFY+4aDT9f@OikLXehC59CnL>XpBkd*ivYZ@+P_R?4IT>pHC-vZ&%>B2g7l zpB*Ccuv#$k3}F()GE!_Q+VGd_?QEpCdiUOyB+Ss>AA|pFRkb(NxL^#xI6fW{L$fZodG_>>z(WdNHvymEQFN&D>YE8yRF$hkxB>M^k z2`UJaRQ66jyalWqC<8k(irh||W6;X5=?zzy3p915i#16s>YO?5{N*=aj5RhhuOtSO zQc|1qTDt%nSLhX=FolrSXEe4KPa`G;a40ZWcnBLklR4&r>aRKffOr&c;M`*xmRB- z?0bTOS5$an9d1&Lk9{B|Yg4bd1CMFk^jipWx}!kZAfSO7xREy5c9KnpClWn`;&c-Y zlncS-Kq2cC^Cd|fE=C#P)|DSPxDkg7d<$Oy-Z)P}3sN~KwIP{B$#ahPP#_lf)!0w~ z)k$X=3s&p7a{X$qIEz#L(oLC7+wR`7YuUza0TPpf_4Nu*&FUg-KRbk z=0ehcVP|XQi`7&n9SrtYYLts@#FN^hEmr0dy%fuurY_|EM<3q*g^`gpS|-pUqX~#C z;=?$`(GLS)9hAqT6`o)~*)&i#F7{M-9z6J@sxd6>HroTZk2Ia*?^R!a?$~qRK3p#( zeT{THIK+CuW(4i|kvg?d@Ph^VQ-g=$Lx>|bJ0e3Cd((yLwPkA)yYIV;V#`#ccbQht zjyT2*^ahlJhh^U|N-@NyJEXx?v@|;#iBJzF5b7jFfw_h=b4nZN3_M1Pcq*CcrBI`$ z7lGu3jvjVh4!Qp5sSgze3JN$wJVa# z*XjmjLDojP*?eR%H4+ef%H|kf;=F@w?OUz2%J_A;YE|W zMUIa8N43GGndzf{@gH7VclTqx+_c z2Vd$}JRMI9E(z8^3AmAGt~fZEhwx&c?rQgX_gB|nd2;>SJiZT464zdmq z1PJ9omN;%x)VCourxfH6A$GIz;G{n~ci?C4*}7}>UEBRsV&+RqK#)L}!W8HuA{$~E zt?E|l2X~_s47I)(H#Q}ZPS88p3-Gr?@#qqn+wh2=c22+7dgJ9|^|EiQcc77l=*ADj z;1tsp05bi(=?nY7+IfNAMsOBk7+?fNzduafYS@R+8DFecEYCE3#bCO;dFPUCd$;Yn zKZq2=)%X?oqm5_^xeS{(Y{Q7i#Y|F*_|R7+)P{XVA7(L$L>x>BB}xPy@q3A=GWWzW z=f!8vy!PU;3nylyfsuG<*zb?lDp(%SZBt~g#zURpXtM&Drw}i*Zvd!MKnYo%43X#s z4uDiFcT1y@-rPyX9U=`fULF* zaq?3GEU3Y}btz>S6vpp<6rqsP%~B&&z*vHr`R%uy7oR(wNvti;ps2H1j_!sCjgAKH zFleYFj3u&ELzoAT5y+yAaCQM@nTJ`?|F`U1mcZwb5L7B5wDFXR5?3KWk+8;eLw{p+ zMFf#{n(lM9?OAr{wR6ojvIF9@tfm;4^(BIs6iyWm!05xpSYZn4O`mx+^PbO0Sgh|nAdlvXDcps}<0FqmZ5Xt! zM4M+-iWH6;S~i~-UflE7A%G0ubiC>A$}w+1e-x`wl$dbxr?V|zs7y2(SC_8|JpB0b zJqI2fS{b6|0)mnwN$`E`;e4cqWV&2ScRo zVV*^cg%Tg|E8M224mn%8SZ!bKA8tMH;QGDy?;Ks{^o|f8K|v77W67Q%ONJ8FtCK(k z%K?}hl0i$W2=4e57{$11AtfQ3E2#s8Btl`OioSZ7>>wxJeBok#HrS~w@#lxr1EaaQ za-;4e8*(h1f}04kW|vkYDD9k1qYbc1L3Sn=o6Rw-aV=a+gQdPf-|qcuV*O5`!nPn~ zyu`p}?G)r=dy=OL@C1j$)5@q!pXnnE5G>EPqC9cidGF=MpZ?)f#|}-js+m?5G-p&O zbPz@5BRBM!cdlMM^2%E;4Li;~+~~G{@fNV4wqZ;| z1}!`88<%7-r+5`|v#ZW42Om^xvlYZ-@X|Ah%i=PUugdM!$dh;0mMz!T{^#)oa-~Y~$u~mZ)e=UMpQ@pDD z4!oMlr)3-IfDTAf({GvdpslO@(H(6$V0WfJrfof)?g3cv%H)x1!AGNFGMm)K%??qI z!;sN1x+{tiHy&(~U7*K>hLMnumQwufco%1KCKX&xZ zSI$+l!Dgj59?8U!(-38iPAWDe{XhO7xgwrU||$TaZN8#+BpqXHL8o^fe#(>V}m& z5XkVsE(eG)SC$fh^+N={!#;WoScVv~rfQ;U3`8xljs}ytXIS3MF&0|!F4wa3)tu+P zTYC5Pi^<@4g=|itBx4E3Q9IOKdRQB^^~+(}(;G8Y8Gdcmpt9kI+?ZQ)*SfKlM#?60 zt@K5iQL(DR5Qdldv343!@x&GqvjV+KoC7~|_w_j$cdxns;jJ6DM0>y~T5dYEQnMY#)d(}XJQxYsRQ82n zAl;KDl$RE1#59XAf{8~xLrRqaSwl-E(PTsmqjZs-l;fv zS504X{`LR;Om!|&oEZpLM-uU#-o!ALim8d&-hq)SQPkM{<|=Wrc_rBxRS2{z`5N@q zLqUFbfo9K?t<`e<+Sowrz+-zNggR4>hyu6X;_?K7l0*ulO|U70sAZ%WiT><~%`RJ)4721? zFRn_(N)n4`tJnZ145AibDtR85R%xVOtWrZ}-$Ut_o{gM3a?$4>>;zKup0A9VmgNq9 z7Y?DF;L1b-a2ywtv0+xQv&WA8@Ts$3A6^w7gb@*k#D!v6O>6g`w&R%YX65DXST6X; zckFy~3A3rigfTzlTa|*5SRp3XZ&=28kTB)d(Y-JqdE*LK>Vm&hL{ua*OfcB0 zSDOCWcwc4dy2$vd)WIjdFuc@B6BLbSN1e*`zEC_x(K27Km)d|hDuF84im`YzsmKP! zJ$Y(1Plsg2qvH%*uvNwOL+S_Z`cGYOUVZ-LGv7UW^iZ}Zx+xVLPWY3-NStlHb`9uk zQ0t7w)Kvpm(s^}gEHwN#IoM{*q8u@s$q+*t!a$^)8Fr`@_~5~v8@3ECL(X8Q9zZf? zZ;&KF&VOmal%cN>7K620yCr6kB*{z{MKmNhLQqxGhq6dGk>odQ)pO4AcbxA%`TncV z9UV-qYZUPnPG*p8$dX8gL4UBXZ-AoSBw$cREi(=nuOy%GUW)|(M0E2UJPvXo6&af$ za&raddWSRH?*UvZzww@VKr12>{OR; zNNnA!yap&fWcX$6VNy^ZhIDRbz>p}UAPPgx4jrt9>m#-RCctA~-g)%BiMh)&2=BNq zCnE{maU^&*xgbUW3nli*+?h(HWX7)IaKYA8|BK%{iwN^;|J_~q3Puo7;8R5PT0{Gt zzJ!kqT-`egPlSU~1F?xQjga%`58GJCnUFy^9t*{h^YLno!P|ak^_pcrI(agbBC~i5 zOE+6brt89MW}}%WdKf|L;s=OJqtW2C`LmgUNW8DMabx81pWlDi&Lp`jBvwkzCTkdJ z7;tnz{Qy#<)hcoj-d*HkXS#qQh#|P52?fZ7sQ$``XiQghtDTaMYTh|<3^VYlcV0a+ zbCtl}fuY12ELqJ8P^sO?Rs)9+MmDI__#wO~0ShfNyqNA|XIqpR^%RQ-PF?9f-&En! z%+%SH8-|vThqvxpzVE>L@s&=Pa>(=_>9mH`oC1FIip_io62eJIwobHJ@r{U$iVi}- zZ!A}m((q#eAAC`e2u}|L+~TBj=#AF%-#hyL+t`K@eTg0Q93?$KI}n6MRN)2&P$FA8 z2LlVcb+Agp+Y(^8v#t>|(zdwE2k9_=Hl}k|`j&Nm@z)+=29)yE#6S%90y2{6m&^cJ zGc?8q0Yw&sYv@81SdzXv@gzl{f9HSs-bu#W=@7D2i#&#Scu+yD#LX$#gPx$k z2Obm?eUNQ(1kQA)QGemPZ{NLRQ~!G68ZiZ`qo&t)@QdewW#*){HNOHd~Cs574TB*Zlm4|#^Yq^nxDAz?01gtdSKMLFmI5)ylYz%=8^2sJ;3AJS;`jaS0cOV)MF$6Eb5_qBV z;iOv6PCIZWQVetc}F#@E*%0fO-*?;;IJN2nH=T1;Q|JdZ8740-+H-m=#|Uwym=-!hcD)6xSe2s z0Eh^N;w&pFTZMT@%M>)klZhDx;YE55{t8>Thj}&UKKSBmoW5{q)t25b{M;j3cP_o> zUVjXckRQi&cVpheDUK*V^lmw9uv>?9iSJoRAf|u=vzeTU$*Vx+Q8&$>3^VC{Zt9@#A_p)$B3oaSG3hGd-4OBKumA8; z;YvH!OvRmW9IF*$AwiMLq}Yme*{dP>pKKejv5JqtC5XKgJ*eigSIDmKR#gNv5Omc9h+4oIjwCl;k#bRWa@}D3p2WVDmDYa;)Jb zm5FzOoe7BLs)pt&wx7P@y!_&|H(ow{_E@=APKKPZM6kD64(1CL^pxSj{wl#Z*b?a` z+ZpJTE%YO8t>9Va_SK-3-g1&-@c};Y#@9^rw0G}Y{a^mwaU}tZGTi9Z6?O-y0Va`0 z_Gu$F$Q6L0MpUVAPedx<9;j7d5DNo&py@~%Es&T&bs&GmdF%D+@k5u+9llnaZsq2& z91ce#{lr?L{VAl!Y#b^WK){3~6edI(Pu2jXPNADwILRQP?gJp)gB6KC>;Fc2Za!2rCEwY6tbN=?Y!{h z;rgsE(FrFU($rUBIh1uC)&xj&v0l0J)vzpcI6C9+&dmdXXfaj|{L0|Ko=E%%KJ5~vH+Sq`F}h0pfxQ6nOh6z*xIj`JktV9u(R68Xs&wj& zn(P3$;1q-F@A4gZV#|NH@=`EDkpnE<4MaRr-x3$bt{K7-oyYQOd<2LhtQ4t;5sLQ@ zuAQAOeCLl2hgz9Ozu2FO^k%P3MN)y@bYHbx^GR{gBXDQg(C7}!DIi#D@J5l3dP%x7(pwK6h@%?%}OFd!?nRBPvZ` z?6nJv3beR>V2WZ+Alrr|_!ga$?@V7ilS?}pmhM=f5pl`@DWe716qHT>csj%@04vY{ zR#{M4qUj3ViUlLnE#JhsOI<(|A?Pso6%*c}$EN^TW{R@b8ll1vOC$pVa0gn-?Kv+yo>Ac&nIF4nhRfiQQ0EIsbj@b_!96x<@$%QvAb{c)b zU=MgDo4sI8+)HsPZ21@tqKP=k;mfr+DPLN}P$shO>hbe``v1P%F7G?|jsBkAp-QpH z3g2#2*v3&YEA2GW3Y(7sV}l8^&xjDHqv+(PoC{~2$t%r2{^K9aT*U*SuRlB%aVX)E zMB8G65sWiDXRdd7-KB;(vGC$P7r+p4Y9mM9nmhbjXZHaZxf6~eakNUgN+O+ttY9%7 z-ZX&N5g-@4v>ipAvGzj|Jt~F|B!Z@M{)ltzaO=&N&rnJ>pGAzIKw@uSEFNg4l4SXf z28#J&tyJ)nq#b8l)JiNd`1~W|a919e0VkB22W~}_aDoK}2~vh6jgTguA?!l`yMKM{ zL?b_Z^3b&({>Q0xn}#C&z9cZuUj|C7n;RUvXiv@IdYFRU$sczHUbMz!tW)@@g2&i1W`Z~JTV@7g?40S=L5S(mPt1BhvkrrLBL2r zjN?rXT4F;sf_DWC=ADy=P9=S@G1aiUXKH_ko zI7xpHo2hM9EtbUW5D9(QpzBTUdgq+-9b+%ObP>q8@ZR_qDY3iU`bm`J90K=D18hH3(3 z%s=J(j5K2+!xP;3FMI@49^Vg!2l%EW%VPWt3vZNjRH9CNZ(9AGH(j}BmA`O{o2bU<93PA?1y?prMg=+(; zHO+#Ph-8ScgPFB{NTAW)u{0$y4L>*kj2()lj^$4BIs;g*^o^t+etaXl^)${ucHH<+ zwPyr;to;0tD;aged@h)3%CIo;N0kLg&jznZeGHI^H2lz`cRl~)#nL=!u}E2?8pyPZ z;jB)AN);8LVg(Xeg^liTI6-O$<|ZY`hSP(k=FH3Axp4mYp}Y4k?-}whU7ewxE1}1- zRoAgq_-keSo>~)^FXZR*lb7eGujV0wuak&}mu(o|+H8>8IYfGwxUV-5Cy_Ye9Gyfo zOmUtcV>g(cLyz%+L-WcXkMyLX!S~;I`~Ub~pMLmD>$mP1A74Wq2FZBLA`(V3)kRb) z5awjsXV%PKbIzYBUOans;$n94at^#B$z3eeA9Rv^$-YRahpAqzv~g=kiwq~QMUu1! z{Vz#hy*Gg2`b8@mDw5J_sCb_uy5mOv~bg@PIOg^vE!5*AloxpbZqfmm;z{Igds zUKyqq?1pWL^f>X2rvI38n2(GnPhXRe4t@ds=g#CWoy$(1uS{MnUcHddO_xdHMX97P z;qbWiwZiQR0u10w5)N|P;3kLpD;h^22|EbHh2m@~9aVN3kOgd&R3pw2R8evQB5CpC zq$DYk6427-F22q1EKntU0XYcKIQaPP(G}7i5h%G3YBxJ&zA}h~w69jpVDJvg04Fh0 zm1sm9C(BY}u;+q2$rcn-LG=m(ti_EW8DbIo0yVRY$(=J+<4MU$cLcIV@P2aJH*MJ3K-05_kLN3E%5GA-6H(Vw#k~bw`j=XiLkz*evT`F`2 z`-V$Jw!=kdw!ES9!qFXKRwnT%-x5g@W7`f{t=wqj`bH!7Jv4y%rB2opoPRMgnLTr^ zC$unu`pNO(9_bv|SZ_397&82gQjrRJNt_ts@%Upuzw*?H$)ks+12VPu!~*1RgMC^N z^jQE5!)7!Jn;@xF8`SJ>48@i4rYP`GOijl*Vh82nhiVq+!uv7~Syr z!tMbo}DZfa`j@CQhu1Y(qUhJtUVS>MyQI0LfNVZt2O2?E?O$Pj1e1mPMoz+rBq!3 ztRmRtkELP3)wz{onk!I>Kh^tSM2DU*$#zmkOwI2E`yD1Hqz#KC~1m}wA4Eu z&E3x$7#OHDvcvtWCT32)^8HJvk4-OGmW-r2cip|AuV1ATRnM%hOIAUqGqtJ9VMZ_o)`*}#VxPu{Bq8Oy1n!ybXfy7aW%o2dv8l+wp`Pj%k!k!KX+yS3qpfud! zp3poYj2DrhL$-K_xII<$^RtEDyU}^#SMIqm^(pu$L}3e5O7uuwyJLhF5fsTcwM%?fuW(vMxP zXj^WIh&@qa`A~$hTgjsohcI6gFT{8_SBx`HghCfE${56RNs3g7-xAJDEb?)Gpf80xCGI|L zcFnMslSiK=hRrFXcrd0KR&MKJjD@|g54R%QT3Gon2s+G#nvc99|PK~XMS6kPLrHL;-_O-dIu{VBnAsHEk z_1d_f8%DxcOKP>msz5v$oX%1~+DB-4wsJX{D*p1{+CR7qpOHG%wpC>V9}>*zR6N7O zL#i3*#H*38G(&jL3eXHiE&HsaWwO4CP}Hi3^Qkp{KgSDxL~$`?N_Q1UkY>p5U` zpIh3T%T+)FL#(la=~Ym^?Nsv4)a9bDn(?>O5EKfsbV!?UC1ZgU#&DQPjHU)*7&Qj) zwaA8)FU|K2WeUwxLrb0Y+cL^u;zzgP0xm|HhZV$8=BC=`Q9Ip(a~yi5Jvas<0GBRI zfL6<2C5`0H1JPg3Kl%s%&oj;0sY)d=l3CNn{}{U_2!r`RKL;FX)hvIEyhH>{oVrF^ zl~@`06GFpPc0SoY>4oV7ovRhOo>X6;GB5BP69JYA5JpVFVK%JT_9~Ma9Kgn86hL+; zW%^Q;Y8ewtwOSh*%uG$qWzv0w|FHlY60`VoLnuU_02TZwfj{H|R@$`CwN(!QeOk^< z2lep!fk?xyB9Ebsy$JSOMR7wz+#F0*Q?w@RTU;rMiKh5%5p{q|8Wg}29JL41LFY={ zDYZIe-7H7a_5HiXpZMCARU0*p`o{e0zuW)ZQ%BdV*i6q#`7BBpdoQe846W$JI2p=F z6fmw$dJt_^BEoQMIm(lIARs4WppE-#?8Hi4%z~73A54$w0 z&&CQrVYpaVSS@G(OMvW;SPe8t?NFp~dHRGC&1M@{?%K9w&#o`8ULO0C-=9mQ@F68C zo-I^$S58;v;A14>E0yYfeZA#omW(b$BBzI&D>jBbC8gs(^{lcsE zC2@|ECXEp5B*=EyC=aCiD8~WgpmN82O{M|=3`hzpS@RI^jGGK{ou7TtMi7+}FAGwm zb~1ypCCdhA%1N-%sVEYX4KZp9H@x%yema1~1ZEz(!>d^Y&q&fzZD$!!=4YHUr!S%< zu~Z;|!(G_U6>Dq=YZf5b4wi=S{F?vO2>+N)B*ta$snIDBT()EHsuk;`Nhl=)O>4*##R7 z61WW2xPT&|Xwo4WA&Ic+f`D{i4-P~u9Q2zrX0u6?hXevFeF#=eLoRIxLQH@q>KWV{v@W4HdFJ0& zUw`9=z0u81i|A;K6x;&tTtFe+n^ZRC7&^3{;@Aa$sIqCx__7r`?{=F@)D0uu@cvc@ z56m872#4Z;kNicQYBfc~M7|cKTyzMBR|Ph~V#e`7sZF9dnK8sN(2B+Dh~a``{HaMD zQtlb+8C^3j`X`)uP~Xq)BLjDBzr5$)Z9>0>bCT%WYtgB73wv?cPry)pjbEcAL>3D569{XS6kHAXqO@ReEBPaZ-l6v3c=VAk9$-Y0 zOSvIH%0_;xT52VNiS4_b1CQSQ>{ExsLrLwd30hJ4%@!+TradqAU=RnOq#R9=!8O+I z+5@l9ZF7XcKOF|A49)qVl-bN*s0(vo7*kRP2+j|>cxQgf1E83*3j=<&s)RcuvmnW$ zez&tNUm=q2Bzs$rKD6gczc4niM0cpqIB&fE4h6)>azU_ziRID`HKe9HFdX+n??yA8 z%gs|swosfLS?%0^U~`b_v<4mwJGsc!eO2FYag1uLI5D+q#HmE#ae`Pt0A>XJF_TFeBhMgMA~NMx>{Q@J)>)qJ#>n(5T$jfhem9Y zCn++o=w{AJJ0#Pop^NNqcqcGZQ9d}mg3sucZz zWTvm8p8CWlm}o5vv^jWWBf*+SV8Vj8K#U|+m|3gr_umRqp(k3k37 zoM;{rnfWTSrAyW3`LQ)X?Y;*NE?>6UsW+T#vDh<|Jn|#wtyj-x!YlhyOXp`<_L%;_ zC36QBrNb($L;`r6YHBrV(3flNnQc2ZQ^m{B8^BWAzMKZ zR*WQx^C|4JKsa-&JMYDFz!J5(hlC;yEdzoPksBW33`uU4<)cF&c})AruqZp=#>&F< z|pi>xt@2@$CHvm+!d0M@$>QgANqX#tm~6j^a)W++l9p?aYV& zh=+8Y`Tqv?qK9nj^bPcvrt3W;;s55h9{(4=^TSIg4rh8+H_A!MP=gUNpt9bgx@fYa zfCvx7q?%1kIsIwYnALzS-*oYdN8vkD5mx&UC@=19jReynPkFUg3I*G#o>;Y(FU(Go zpF6X}`N}Wu8($iD`a7Lt?_SJA#(j+xLV{8z8PN>da2NUmmh0h-a;Ij^c?hF;NQq*2 z8=3AJ%rf7*lLcW5-*8z`xQuM%JckQjX6V=j#OB1<7)r8F6{`3G^EKZTNu{=J7*?*! zy(8l*9Vg`w%T&(IQ{FTh?Pm{+Vje?DI?MCXA+uWt=Wrf!G7gL!G2P2T6+1|*<7xaQFyg7BpLh`bt1@!MEoOA%6Cg#MqDlg^pk3l zaY=O6?_J-&YZVWEiO@#}ZIv)+9QfOVZ9LJtibs~$#lwuqQono9(x zut3%G+P`PYa~Zl30=MKStV2?i_zk8ac$ZaLBc%VpxL~>7y1HU>^vmDa+dq!DRcEQj zr;(+U@e?OY7m(|y7{8X63xZOP-BsCJl>o*uP{W zmz4>3Z5;HQAr5fp>0>ijm zm7(hC`X$#PMMN{jQD|MEPb&bq z$?GI|;SBCS&J!o}mq@ z7Ls|r-N|>_@khQgbouJWw_nKBa>QV!86?unse@XL=o}NwwORsQICu-i=&gC>5fPbK z8w|oKojh+4VxhfG1;cepoSC7QYRqHG0f9#b!Jv0Qra*cYx zJNNJ!uwcFq91EBwI6zlIw_pv#Mvf#;x;WHux#=>z*dL6fEaN`PD`XAd+g!uyAKe{)(KL29Zwf_xL9gX9cUVsVjX3Wn7Z9fovtqNfo|RKK>!0gxCh3{ zbOz>$#IP83`o^Q>Y<|@}>A(9AAN$>Z@~u*}qz$oVm_Qv(F_~j<=mUUYD_N#_L>gn4 zT|8~3y*O7Skk|-h8zDeuBUf!CIf>ER$R=0M)#fAAOK!}UYm=j^Vvii${@^1cy=xQz zjFbAhea<5jTYm4KJ)KOg?lcMke@2#Z;lE7IOet1 zwn||FAGH$sS*=vX;qR6=zC*qU{Pu7EwwGNM9XZUZ0;zSF8H5BRE~`^PvrUz~*th@e z4gB>Ik)#}fNU2S}v7qv75bB8)DH8VM8p9A0evCg0VhFoom=*KQ^LIa#`Q^X2CpE%K zGFPu_r;J>EBwrV3Na;c-gXfXA=mc=5;Wn?`zPLSdKe+D~*~=)b0!(HNjj$ESei?gN z+&$eJJMieHT)uVW$niq4i03K>aEc+qzPQSuBC_$PP(ek;;K~%Ht}=BQaKTZ+5KTcr z8&v}$m=!Qybtw8?CvFgY2}KbQfxU8}ajEQ_=^3AS=u1m~>DTYw`{mvk4nTB0?c^#s z5)H1|6df2GJ9hHj>B+0uIO4HPs}^k36tIY)2IdZjTbUFtU2N@%)PN7WYwQI;Ri?9q z56KAnrvrIR@qKh2vlsFU6M-K|jzQcbXJ)lC-wMq)Lz7PO+R)0zy$8pB{#UpE!mo{w zZ*)@q-6?E@UkC-oh00-kp>TsmNnxx7+HFRV7*fC7Bf`mye{9*3d~tSq<{Eb?7x4c^ zYfJ_pSG_~_{WxY!JurQvKd3y~1T}q<1kS;;a07Nb^hA0B#+zEUWEQ1R3_>KnlB@-M zLPaVDVx_w8YN39*)H#(H$Zp%y|J7gI^-F(i)23Yka==sm#vdcZJI@M%hp9{z^-wyQ z`t~1WpZWIbf!Ic0Jyk8WqoJfqSvo2SPN2GI2shF5LS1UFQgXIWOZiFUmD%LZr|T{BDiH~J=`i0{e|`qzd>BKL8UixA06ht z=H4ItxOo2deB-r*rO9cYTnmu~W`R(+Jr*dK9`C1G0T8u(ZU42Cg{jbUZf=YrE*<;4C?_-Rk z+#LP}wiZ{#n-7G`QH$T1LF5H^qN0hGGd&2eT&LVawO}gppZ)5FRm)cXQ+2>mhHYKx*>9z*IU0+{sP|5aY8&Vp76K zuvH-))?3v^hYDXcAGwty`9yzf>8j*iJJ)X8y>dD6JrsJV6Vk$!A{(o{F1(7}Na zmIr)MB&=zXkSSfvR>jv8uYK~oL?c`Fruzo&dwSsgr~mwLynjWY6)cp?e#(9NV})XN zrc9dI0IH>SI&rE1mZUHvf=G|X&7ww-1TF*uUO^cbGfuO)Jc}8{L#X}U@KtLL8CpxE zOPF3a<9GaC4Qw4qHGzS&$m2nmjCWHM?@HIbS6I#jhPmPzRGXpsOEGosV&_KH$rt z?9|vAVPGDaghM^_D7EEF`*KGw%pW;Bm(50kqz%ErK{=x_PmznhL8{15C%$fI&GvO8 zJ2yM4Nq|M&VU`_}KT9TE95@hN%}&9`=+SL=sn>vI0IItp3bVT7$B?6MD2*BFhBG+k ztll_y{N2L?nXMIy29#NjTjZ%GMG1>_y+(32tjOVXdSG()TCPxv2dWcuM^`=ZjSU|LPA%%CP2F=ff6%4w&xOvL~a92NGX~VrpFh!JuQb6$lfTlKKkek@k zXWUUZSw;7MIWjQ1|2u#B+Kb;mF*A8_Fts5V8ls++QqYjP$S0>nIWYzUg&GBfMFHj& z)x$7*1%oMS!B<){wQ8=O!+_09?xcsEb!*~FmiO&{bo0Qt4=k3W2J?YP)&MJXg=hc( zAt^~jK~!1{1|s>;URf^Asb+$p$_qlp!E*P&S`kUkz+jal? zbz8>nx@V|oiIlS{gb6z;pH(kGTcNWu)o=qgPndB`d0@Q`P%)G|fK($b9e$EB)=5`W zt&qU9Cy`#U$$6x?dT2@bPyfH?i@9r+GUWq@GO^)0B{KZ=c&evZ%z-^Xk+f*Q%C#h5 z5v)lER=ct(Ny)|^j&GA`vRXGP&3w5yTWe1F!X+|l#WU^4p4hNz-N2S@OW6}3h*GRU zFe^K&RF4m9=ZwuiGU^arD8H>HxjFo&kLFJtJ{$J+HES3vP^@Bj3Q!=B-$E{MlMcCT zL%c&Y)t<l*iu|Rv#%Ix|EokpnBi1~x@}-0F(rV4C^T$TlhJNl>_U`_I3LyA!s%9;M=U7u^HfBb-@Pt6* zjuhXTdoy@lIJyZvUFPG91kH1vYv($k4ovUBOOHKBp!5=NjODjvSzSXl_ntCcI{J7ffq-i-9g0;Ac9$it2F zpE9+A1^|l&2*|2-s(42LmqbcZ%>if`T@_ige%<(Z?~{M>+Ns0YM!t1*?pkkpczASR zc4ji!Kgil99SCGHVI{Kn`F#zlgd%lG-37*Ksv_~1WCIS zhT??VOIk20I%uW780P&L{khF9)dcdoODsg%UPh?W4vT9IVoQTk2jygxfW2t5=nzQo zQtRa5+U!El#oI7$Wu6R{=Zx6nto?LMVhW8BHZ85h^|>SCO1-ZaFN(19d@t4>)1 zj(pGPJU%gV*P#%JXwY63J2c2J2PWpNNDL zD7j2JTQVSG8LS#ik(boz+tt z-1qfyFYZu^1PNZexC99lr@@;*af(BL;>Dpj1zIGyySuvHQkKBvVx0Joa@a)QsQ7a4k6g zF{~YD;s$-&P4gtiy1XRQ4=f(87paa7H%;db)3F2@m?fD9x4axA9n^3G(KWc@p6Kf$+#;W4yE6t?qc2l8ufqqCEO8*d&5)pkommv-DNGP3q2z7&ulBZ614fpUS- zr(x_~WehLEcPus>=e`dZb>LWTO%pTQUyu}@IIY4)tC6HB zOw4A3TaZN{4x-1#4aU%<4cdcTK74n03OF?;vPh{@U9ij45&OPHo6jxP+@@uNBUXPJ z!60s;aUcLl{)|WtZkhPYE`%CglhD(V)+?W_j}uj6G`};AlCJ8?sZYVCx&30G+m|QA z2aWtvy^L%}|A#u3Ebsef7KQrLg`=L#t2BSP_yQ!$}Ksdu=- zGXCUsm&f_k^U;}A3DsxiU;yu#aOn~8?OEd4$R7S<-}LS1?ezRF=;PJU@iG-tI!-7z zFmym%eO>sn5?-Vdg(>h2CV}I=1%l?huywC5>pj%QuFr?JglLM_{dnCS(`WKwX*2`d zGKAcy24)Jv=f;jwG?S~`LPhEO5l~$c!yL9hbZ+cs#VcX0Nil^$Fo}*Gx;I2o6e!5r zV<16dOS$w;oXA(S;4g7RS1=UQ3 zMYWqZMaEMkS<6l5>%4-paB8!Y5bdqzU;K_voutpq{AMF?w7Y8$=p_NQSV8+L#oqqC z)R`J&LWMHFJUUP(h`r=7mKGP zHsifJaXZUi_`7J-O|T&{`c1~`5s;Dy1{o6vF4b#7W`OSW`$WYnRspF)8H=#6``EJCz#Z&%Y6- z4^Ie&NRL6|0dylo53T}k;yN2otaY2A^K`dH z(P_=UssB|dnN#Pd^7jPS1*yt?G?YD~W*YCD+;tcvgKvOt(=^d?s8rOO)w$O(W&dg%n5X zxW~%0(4lm{Z4Sngw}HTEZ1{fxLH)xx9O?#*Ow5As+`jHbcVrr3KBcX3#|Xx8`;h)% z?H2HyHKky3>8TU*uY0q5~= z1@P)WzRgAD1%nvl!}yc;UI2WGAt#$Hs+Uo|9dEw0e|9f_RBuVNP1XKZIqaj*C}>E^ z3L3Kxp1{IjCHMM)h&Wv^5sG(@ibPg zc&->rqAEl$FN>V)MQ_9sy9$W^>z^(2f?jFjf?-3U(RU(XU+wO)9u08__Z&LSftOcb zVwl*PC-eME&or6~;3ci}F@w2{baz^)j`Y`vhKKP0$-~1Y z%xZ&p%Qt5$6-)E71;Q9^qD?-e=+(bZOYSlvg@*W<~eyNWn2D*FISu$TN;gt>6Fj5obd1Ou=p+^R3Sw(!xk#c)9u*2oNcu_P}bn| z1g)Y>6g^%-ZbR^NvJ9TysRW05- z?hPah?g#zg-3hUKfX=A5a>-I50BF1x%_OSB<-BB@mc}T70N(vKUYBG7O7eM@J>2&wtKGhyLovf>JCJ$e81^aVfq%XQA1XMKwSx?^d9w;a)tSS`UEX z+jDXYrl#-FG05%rA0i^so2h5pLaCand~*+E;3_W|&vD7nrJX>IXjMjCV#9auQiai! z&K2X|QvB7#T!gnM<`}UhL1lkm6p_6iLYvNpfApxLYP|0vK7%c`L~Im0rfY19tJ=VY*^anK5c zo{uKGVq&=^DYCsrIR2H_y&OIIHPLxeOGS06DJ7D*bz(l7F*oJX?EI=a+GUaNMyu}l zLprv$8}EHqL5NumKcQ;uW!Q2gq(;0w~_?6ib?p;~k0j_=ZG z_z5~rl9zO|xtuhuQb&ImO?WDuS3IFk4<$aCZHzR$Xk!E`J9sU6oP7cyNx<@|wWe6t z?RA%7x)Dhw-@uT(e!@}`W)T_cG}i>=L@T3WdT%chL%`golcAlzvGXGCX^)BpU8DXL z3x<{IwljG{b>wpn(W`vfCf(@1PaoJ@AX2ILQ5DD|uzKIHJA(B$m2Eil47#be^;x7Ct*Rx12auG87O zKf~bRiOq>JYpW0HLe<*oTt(OEvFm!xmH$lFY2;;p88^U3X}IEh)Hqybb`T2v<8)1@ zv^iL}xUsSAy~gL{@^}UH-vm0MbRK(u%b$Do2>UhvrO>C)7?HO^zk8!m^$H7ejfP|H zztZuL63|>^!C(9FO^jF6dCd-}d1x_vGVx-xZt+~V)2nSrnOOl~NuOp>XY~kz5^b)i zR*bv0q+~CJ37+8p?mc_x42QFRqKE3F&!Ex#OEHSEuMl2)K*4leX1@qk3kqGHX2scX zR(djA`;b5vBul^n7W%J5LpRXFQ@2Eu~G2El9QhvS)zQNsDhJK8r6~ zTDMi+#b8GXep~y`lt~}EMla1qg^VA=vA5TT1U3}zg0#wWd0d6E*SzmKFS=N>#xiK4 zrAC_mi}S*jR0qS@bzpyms{4i1TcUTbUf{c!G4Cb!Hy84p-bcKemT> zNs=4gWX|DrHg9;JeH%-iqRj1ad9`d3$Z6Uw1Fs`lGf8JFtki*W>Q+)Lu{(2L76UC1Cpr&jb`dIQX{P0lZL@oJgYlX0yo8SB2 z{_k?SwyX%q4BvQ6dUqnVkQc!dN?Z21^9wl3@et18ue3fS34o&40zrLWjT$E^%iSD? z{Mu40+52;sW3}oMJ5DR#vW}=vgY-U{^be)4ZDif2k>A*-=EW^%b*$=aPUZx1FQ?6? z%W?kHP#62R1IGD-S|xM{J(ED=QHf4S!ry9z)>DLD%bAhI)}t)R>26mE(hrwvPW%|^ zVF%}W&0Gm>Zd1X>Xsdt4Mc2$Y&VV4tbdW84iw6;9-R9+Sl1P zeW?WO>;5YJ%#qsxK8ssZbHQ#cql4d#3}e9Q*Ie3&8=rCvqb>nTr7arYU5zYE-`&KzuE3Jye=rY+ z#vi%X&}zolaivBqcAm%hL|RppsNxcyGBKm#ObPh)Wik-x9xk(+yA-L5KOp|jA?l!O z@W87#c~yQHE8%F-ZtH$5r@b$eKI*9N4zFB&i!>_A%JpKV0gS}UZYRiZMA{}gp(XHu zPJVsch#dSJ`m=ntwGr7Pj0dqsODIF@@-dICfdEyFpG@16T1y0*h)|`8q+03*!l(Lh zmaK2cfz=!aE}DBj+siH)TY$-2W$UU$cOGykw;$#tu}oBgDJ9yd9PUU_dMvcQC z7JzqePH+B>^H1W>5ucJ&S^$6q*TwON1hfkGFQjV`TP*&QPL{0YQ@1E9L&o_iiKNet zdKKHHKU_50XMLY1qK{NpH)4~FRDp~Mt;4-NsaU^OaqPCtc2V#U zyZJ;`+Zk3jlQ-1RaWIDdP~<`Pk2bA#^4GyLz&&6u$bNi$DThi`p@sg=G~tj(2`hIO zuH7BnoU0_#IUHNhJ78TuKGdWN-U|i0yvTi6N zFP!(zxMQzhCCh>kwKj{Sr5qr{xtH&Lou9%A)c6-VsIt0=Sgv+JE@Xn^BizF*UI7$I zg~x&1PT9E0TkMBzc5BPXE*08&kC42&Y$D)g(mi)vdN-{M5^im+sRlMl|fMi)& zn)YP3-$~m5&x2p@;S=MlXK}gWjlNfOeu0bq=)tacp;)E?*ua@8UrJuD4koYRyaFW( zkTTBOtFt1eW3zqSj|_ea%~c$ofpxk|Ll6LA2V=w=OiGvn!@iP~!ry5*T%zPU)%RaR z0uvwusb4}6zWduF$O3mewIYf#NQvwA(Dr)q9qAc4;lUi3#quLSHRaO0KxmpUHIe1E z>tf?M2L8E!t_OvpJ$}gO$-O<*IIX?BHS~73aG2kp5b;J)8v61d%UyfPqgj(8l%<_0 z{bDQf(gIm<__<;~H*!K&dRTSc`{=9Z&k3&%4ft=RJVUt-mJ&(b&xxhmjfK*VY_o!h z^o!7gOoN)$mYa*Nb7I!a;4>@6nQW!<#1b+_btwwgRrD;A?$&j5Dz2)4#uI;YJcI7L zNU#lEGZ}~MciUE6Sb{ulu=r$dfhtex*qJnK?3ER1B)~v1h;9GR<}@#MsOvQFw=w!- zUcQ@6BPZ1BaGlT38y9%{u6xj+$7?6sklWn!pU;JCltgPBgta{{dj&4L|MN(GyZuu}dBfL~?1HuX zbg0JotZL2CK1nN~Xb7bLKP>b_$C_(-YbbPqINtBY2;d`s7_@&O2rYe7SWB}ryf~jW z^L(DSmm=Zby;NZKJSmt_hf>LR$t*mFBI5mt*e{+8&-=&hogzw7yLQ5eKY1vU9$3XB zWyU=F!N#~HL6A0q*KXeKJF1JNu@ZYRIF_(z zV~>*y8tD_wz6u%HJ46_ElkAI_+zw3WBHDYUH+-%B$0{cae3HzqdiG@`+{>l<<$6hzi2y<&Y zPGi}#B@el)<)#As;965=&Z1bY;9~hf`OzLJRw@u&8pkn&dFv6RpIBt#wcqJEiZgiE<+6)e4bvOL;RsN%1sbP4!m8n>6uKxoTN1yuKO^|Ev z2_z<-D#Q+)_PxQN>n49euTOu2nkl+9-ZaB?%4OzZ+ysVNrWU4o8P{Oum%Cjbv-X}G z<*Ug!X)Vs%FEtNg(mJ1rPq@LxX#B9EXr*DjLx#qi8W(IA)jx4PG`=9@-xkY@Vb$)ohC|(2B+? z)Fqnwtxx`rDC&FlHXH55G;2K$7fOQdoi*X2e>j$*Bwmpz2kz~RS1 zKMzZuL_^%^o9gVn5xqFJ=7>*)G}6;38o9+`539pQpSk?)X+?OPcRZ@+rdH`Z`1p^O zQW42$kls5m&?X?jS{ubiAOiWfHRhRv9fkWBF&39xtlkhEY;fSEe2HbFXbE-mAZ9&X zWouor^U&V~w1t-v?v2P-;b^@3)b*~R%-0xI>OsWwF7B;dV6e`8d;`9$_0U@%+j0%haOEbn3O&JmItqVFu%4goL8!=+8HOA_$&FjF}-z7 z3E>Y)ovBCCS9w;lhp1$0KD-`@ziRPu*W~l959r7VY%udy&|lfNR!04Sb=f?B@Y#0c z2uhVd9!__51nGD_J8<$SkUGKruaV>-E5!wY1%Xumv7^#!a4^C%!1Izv#W%=`pBJ$M z!hoBY|LNr3trY&JV<)c;+`%<;$V)*>L3^*&jh6DiL;nNuzl!+(pf#SJkLH+AQBY6< zv!AO>&5(gXjHqbQfo44AxfnR~Xki}3cBG8eIMfThoT`D{OfZA+QnvpI{$CELmLN79 U_nkbne<)GFiW;CwdD!Rw0}V=DFaQ7m diff --git a/blueprints/reef-rpc/reef.svg b/blueprints/reef-rpc/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-rpc/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/meta.json b/meta.json index 48f68dc7d..aadd7a98a 100644 --- a/meta.json +++ b/meta.json @@ -4304,7 +4304,7 @@ "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", "docs": "https://docs.reef.io/" }, - "logo": "reef.png", + "logo": "reef.svg", "tags": [ "reef-chain", "validator", @@ -4322,7 +4322,7 @@ "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", "docs": "https://docs.reef.io/" }, - "logo": "reef.png", + "logo": "reef.svg", "tags": [ "reef-chain", "rpc", From d915a3eb77c5c3e828262418089fc2c0a59e61b1 Mon Sep 17 00:00:00 2001 From: Anukul Pandey <62092256+anukulpandey@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:55:23 +0530 Subject: [PATCH 13/90] Update template.toml --- blueprints/reef-bootnode/template.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index 3a1a98110..d2c1c483f 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -7,13 +7,13 @@ CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-te [[config.domains]] serviceName = "reef-bootnode" port = 30333 -host = "0.0.0.0" +host = "${domain}" path = "/" [[config.domains]] serviceName = "reef-bootnode-rpc" port = 9944 -host = "0.0.0.0" +host = "${domain}" path = "/" [config.env] From 0c600c66607cce1c9895e13af54c6c6660ca1e89 Mon Sep 17 00:00:00 2001 From: Anukul Pandey <62092256+anukulpandey@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:55:58 +0530 Subject: [PATCH 14/90] Update template.toml --- blueprints/reef-rpc/template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index 927a1a904..311e92634 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -8,7 +8,7 @@ BOOTNODE_ADDR = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuV [[config.domains]] serviceName = "reef-rpc" port = 9945 -host = "localhost" +host = "${domain}" path = "/" [config.env] From 9fa625f8938a8d29deb83e428a518a6a9862a6ee Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 8 Oct 2025 12:35:53 +0530 Subject: [PATCH 15/90] fix: service name --- blueprints/reef-bootnode/template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index d2c1c483f..312075761 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -11,7 +11,7 @@ host = "${domain}" path = "/" [[config.domains]] -serviceName = "reef-bootnode-rpc" +serviceName = "reef-bootnode" port = 9944 host = "${domain}" path = "/" From 13ccca6424c59ba67951388f0ad570aafb7af1a9 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 9 Oct 2025 14:47:23 +0530 Subject: [PATCH 16/90] feat: reef-faucet --- blueprints/reef-faucet/docker-compose.yml | 21 +++++++ blueprints/reef-faucet/reef.svg | 68 +++++++++++++++++++++++ blueprints/reef-faucet/template.toml | 19 +++++++ meta.json | 18 ++++++ 4 files changed, 126 insertions(+) create mode 100644 blueprints/reef-faucet/docker-compose.yml create mode 100644 blueprints/reef-faucet/reef.svg create mode 100644 blueprints/reef-faucet/template.toml diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml new file mode 100644 index 000000000..9045fe387 --- /dev/null +++ b/blueprints/reef-faucet/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" + +services: + reef-faucet: + image: node:20-alpine + restart: unless-stopped + environment: + - MNEMONIC=${MNEMONIC} + - RPC_URL=${RPC_URL} + - TARGET_ADDRESS=${TARGET_ADDRESS} + - AMOUNT=${AMOUNT} + volumes: + - ./faucet-data:/faucet + working_dir: /faucet + entrypoint: sh -c + command: > + "apk add --no-cache curl git && + echo '>>> Downloading faucet.js...' && + curl -sL https://raw.githubusercontent.com/anukulpandey/reef-cluster-registration-script/main/faucet.js -o faucet.js && + echo '>>> Running faucet.js...' && + node faucet.js \"$MNEMONIC\" \"$RPC_URL\" \"$TARGET_ADDRESS\" ${AMOUNT:-1000}" \ No newline at end of file diff --git a/blueprints/reef-faucet/reef.svg b/blueprints/reef-faucet/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-faucet/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml new file mode 100644 index 000000000..d253ddb71 --- /dev/null +++ b/blueprints/reef-faucet/template.toml @@ -0,0 +1,19 @@ +[variables] +NODE_NAME = "reef-faucet" +RPC_URL = "ws://127.0.0.1:9945" +MNEMONIC = "YOUR_MNEMONIC_HERE" +TARGET_ADDRESS = "TARGET_ADDRESS_HERE" +AMOUNT = 1000 + +[[config.domains]] +serviceName = "reef-faucet" +port = 9946 +host = "${domain}" +path = "/" + +[config.env] +NODE_NAME = "reef-faucet" +RPC_URL = "${RPC_URL}" +MNEMONIC = "${MNEMONIC}" +TARGET_ADDRESS = "${TARGET_ADDRESS}" +AMOUNT = "${AMOUNT}" \ No newline at end of file diff --git a/meta.json b/meta.json index aadd7a98a..b3a31c5de 100644 --- a/meta.json +++ b/meta.json @@ -4312,6 +4312,24 @@ "evm" ] }, + { + "id": "reef-faucet", + "name": "Reef Chain - Cluster Faucet", + "version": "1", + "description": "Faucet service for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "faucet", + "self-hosted", + "native" + ] + }, { "id": "reef-rpc", "name": "Reef Chain - RPC", From 6453f1e506b83430e8070b1f4a8052fbc30a353a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:15:16 +0530 Subject: [PATCH 17/90] feat: reef-keygen --- blueprints/reef-keygen/docker-compose.yml | 44 +++++++++++++++ blueprints/reef-keygen/reef.svg | 68 +++++++++++++++++++++++ blueprints/reef-keygen/template.toml | 12 ++++ 3 files changed, 124 insertions(+) create mode 100644 blueprints/reef-keygen/docker-compose.yml create mode 100644 blueprints/reef-keygen/reef.svg create mode 100644 blueprints/reef-keygen/template.toml diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml new file mode 100644 index 000000000..b4d4a626e --- /dev/null +++ b/blueprints/reef-keygen/docker-compose.yml @@ -0,0 +1,44 @@ +version: '3.8' + +services: + keygen: + image: anukulpandey/reef-chain-node + container_name: reef-keygen + working_dir: /workspace + volumes: + - ./output:/output + ports: + - "${PORT}:${PORT}" + entrypoint: /bin/sh + command: -c " + echo '📦 Installing python3...' && + apt-get update && + apt-get install -y python3 && + + mkdir -p /output && + chmod -R 777 /output && + + echo '=====================================' && + echo '🔑 Generating Validator Keys' && + echo '=====================================' && + + echo '➡️ Generating validator 1...' && + reef-node key generate --scheme Sr25519 --output-type json > /output/validator1.json && + reef-node key generate-node-key --chain local > /output/v1_node_key.txt && + + echo '➡️ Generating validator 2...' && + reef-node key generate --scheme Sr25519 --output-type json > /output/validator2.json && + reef-node key generate-node-key --chain local > /output/v2_node_key.txt && + + echo '➡️ Generating validator 3...' && + reef-node key generate --scheme Sr25519 --output-type json > /output/validator3.json && + reef-node key generate-node-key --chain local > /output/v3_node_key.txt && + + echo '=====================================' && + echo '📁 Generated keys:' && + ls -lah /output && + echo '=====================================' && + + echo '🌐 Starting HTTP file server on port $PORT...' && + cd /output && + python3 -m http.server $PORT --bind 0.0.0.0" \ No newline at end of file diff --git a/blueprints/reef-keygen/reef.svg b/blueprints/reef-keygen/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-keygen/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-keygen/template.toml b/blueprints/reef-keygen/template.toml new file mode 100644 index 000000000..23950b726 --- /dev/null +++ b/blueprints/reef-keygen/template.toml @@ -0,0 +1,12 @@ +[variables] +PORT = 48765 + +[[config.domains]] +serviceName = "reef-keygen" +port = 48765 +host = "${domain}" +path = "/" + +[config.env] +NODE_NAME = "reef-keygen" +PORT = 48765 \ No newline at end of file From d0277ff23935ac398420dfa442005d40b7eb7f15 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:20:02 +0530 Subject: [PATCH 18/90] feat: meta added --- meta.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/meta.json b/meta.json index b3a31c5de..eccb5a753 100644 --- a/meta.json +++ b/meta.json @@ -4330,6 +4330,24 @@ "native" ] }, + { + "id": "reef-keygen", + "name": "Reef Chain - Keys Generator", + "version": "1", + "description": "Generates 3 Validators keys for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "keys generator", + "self-hosted", + "keys" + ] + }, { "id": "reef-rpc", "name": "Reef Chain - RPC", From 3766185d628a07eb8f7c3d8ff3f580d6d0a6977e Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:26:17 +0530 Subject: [PATCH 19/90] fix: node_name unused var --- blueprints/reef-keygen/template.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/blueprints/reef-keygen/template.toml b/blueprints/reef-keygen/template.toml index 23950b726..4fbd545e5 100644 --- a/blueprints/reef-keygen/template.toml +++ b/blueprints/reef-keygen/template.toml @@ -8,5 +8,4 @@ host = "${domain}" path = "/" [config.env] -NODE_NAME = "reef-keygen" PORT = 48765 \ No newline at end of file From 9bf92edccfa296dcfe4b228e0d76dfbb3c98d644 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:26:49 +0530 Subject: [PATCH 20/90] fix: service name --- blueprints/reef-keygen/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index b4d4a626e..bccc0018c 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: - keygen: + reef-keygen: image: anukulpandey/reef-chain-node container_name: reef-keygen working_dir: /workspace From dc7bf2eefe91e1ec1ec50d62e40692a04ff6256d Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:33:39 +0530 Subject: [PATCH 21/90] feat: add platform --- blueprints/reef-keygen/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index bccc0018c..8b9ac8db7 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -4,6 +4,7 @@ services: reef-keygen: image: anukulpandey/reef-chain-node container_name: reef-keygen + platform: linux/amd64 working_dir: /workspace volumes: - ./output:/output From f47613bc2ee19dd46cf9c9cfefa65bfe63addd60 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 13:37:47 +0530 Subject: [PATCH 22/90] feat: dokploy compatible --- blueprints/reef-keygen/docker-compose.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index 8b9ac8db7..b457eaec5 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,10 +1,7 @@ -version: '3.8' - services: reef-keygen: image: anukulpandey/reef-chain-node container_name: reef-keygen - platform: linux/amd64 working_dir: /workspace volumes: - ./output:/output @@ -12,6 +9,8 @@ services: - "${PORT}:${PORT}" entrypoint: /bin/sh command: -c " + set -e && + echo '📦 Installing python3...' && apt-get update && apt-get install -y python3 && @@ -23,23 +22,18 @@ services: echo '🔑 Generating Validator Keys' && echo '=====================================' && - echo '➡️ Generating validator 1...' && reef-node key generate --scheme Sr25519 --output-type json > /output/validator1.json && reef-node key generate-node-key --chain local > /output/v1_node_key.txt && - echo '➡️ Generating validator 2...' && reef-node key generate --scheme Sr25519 --output-type json > /output/validator2.json && reef-node key generate-node-key --chain local > /output/v2_node_key.txt && - echo '➡️ Generating validator 3...' && reef-node key generate --scheme Sr25519 --output-type json > /output/validator3.json && reef-node key generate-node-key --chain local > /output/v3_node_key.txt && - echo '=====================================' && - echo '📁 Generated keys:' && + echo '✅ Keys generated:' && ls -lah /output && - echo '=====================================' && - echo '🌐 Starting HTTP file server on port $PORT...' && + echo '🌐 Starting HTTP server on port ${PORT}' && cd /output && - python3 -m http.server $PORT --bind 0.0.0.0" \ No newline at end of file + python3 -m http.server ${PORT} --bind 0.0.0.0" From eba189654d823ad7ab62a59fdef0ab833f0d5a9b Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 10 Dec 2025 19:30:14 +0530 Subject: [PATCH 23/90] force: platform --- blueprints/reef-keygen/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index b457eaec5..1431c48f9 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,6 +1,7 @@ services: reef-keygen: image: anukulpandey/reef-chain-node + platform: linux/arm64/v8 container_name: reef-keygen working_dir: /workspace volumes: From b8dd5e257f98834abf3940c6e7166cdacda4f269 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 00:52:03 +0530 Subject: [PATCH 24/90] feat: linux --- blueprints/reef-keygen/docker-compose.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index 1431c48f9..6bc17e17c 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,7 +1,6 @@ services: reef-keygen: - image: anukulpandey/reef-chain-node - platform: linux/arm64/v8 + image: anukulpandey/reef-chain-node:linux container_name: reef-keygen working_dir: /workspace volumes: From 8095cb332078e71cb8ad20b8519b7105edda8a5b Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 02:19:51 +0530 Subject: [PATCH 25/90] feat: linux-2 --- blueprints/reef-keygen/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index 6bc17e17c..ff5afc58e 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,6 +1,6 @@ services: reef-keygen: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:linux-2 container_name: reef-keygen working_dir: /workspace volumes: From bbf5745f30886268a64acdd4c0ad2de57833f217 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 02:32:57 +0530 Subject: [PATCH 26/90] feat: eth-rpc --- blueprints/reef-eth-rpc/docker-compose.yml | 14 +++++ blueprints/reef-eth-rpc/reef.svg | 68 ++++++++++++++++++++++ blueprints/reef-eth-rpc/template.toml | 13 +++++ meta.json | 18 ++++++ 4 files changed, 113 insertions(+) create mode 100644 blueprints/reef-eth-rpc/docker-compose.yml create mode 100644 blueprints/reef-eth-rpc/reef.svg create mode 100644 blueprints/reef-eth-rpc/template.toml diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml new file mode 100644 index 000000000..ba4f62ff9 --- /dev/null +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -0,0 +1,14 @@ +services: + reef-eth-rpc: + image: anukulpandey/reef-chain-node:linux-2 + container_name: reef-eth-rpc + working_dir: /workspace + volumes: + - ./output:/output + environment: + - PORT=${PORT} + - NODE_RPC_URL=${NODE_RPC_URL} + ports: + - "${PORT}:${PORT}" + entrypoint: /bin/sh + command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT" diff --git a/blueprints/reef-eth-rpc/reef.svg b/blueprints/reef-eth-rpc/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-eth-rpc/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-eth-rpc/template.toml b/blueprints/reef-eth-rpc/template.toml new file mode 100644 index 000000000..1b0122ef2 --- /dev/null +++ b/blueprints/reef-eth-rpc/template.toml @@ -0,0 +1,13 @@ +[variables] +PORT = 8545 +NODE_RPC_URL = http://reef.host:9944 + +[[config.domains]] +serviceName = "reef-eth-rpc" +port = 8545 +host = "${domain}" +path = "/" + +[config.env] +PORT = 8545 +NODE_RPC_URL = http://reef.host:9944 \ No newline at end of file diff --git a/meta.json b/meta.json index eccb5a753..6091e9ea7 100644 --- a/meta.json +++ b/meta.json @@ -4312,6 +4312,24 @@ "evm" ] }, + { + "id": "reef-eth-rpc", + "name": "Reef Chain - ETH RPC", + "version": "1", + "description": "EVM layer rpc for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "eth-rpc", + "self-hosted", + "keys" + ] + }, { "id": "reef-faucet", "name": "Reef Chain - Cluster Faucet", From 0a9d59bd072f395ba87b1732c2aebd513db2d1e3 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 02:33:29 +0530 Subject: [PATCH 27/90] fix: image --- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index ba4f62ff9..9cb09b896 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -1,6 +1,6 @@ services: reef-eth-rpc: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-eth-rpc:linux container_name: reef-eth-rpc working_dir: /workspace volumes: From 4d36eb5d53880e5e6ec173612db22ac905983d5f Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 02:36:46 +0530 Subject: [PATCH 28/90] fix: variables --- blueprints/reef-eth-rpc/template.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-eth-rpc/template.toml b/blueprints/reef-eth-rpc/template.toml index 1b0122ef2..dfe5d0483 100644 --- a/blueprints/reef-eth-rpc/template.toml +++ b/blueprints/reef-eth-rpc/template.toml @@ -1,6 +1,6 @@ [variables] PORT = 8545 -NODE_RPC_URL = http://reef.host:9944 +NODE_RPC_URL = "http://reef.host:9944" [[config.domains]] serviceName = "reef-eth-rpc" @@ -10,4 +10,4 @@ path = "/" [config.env] PORT = 8545 -NODE_RPC_URL = http://reef.host:9944 \ No newline at end of file +NODE_RPC_URL = "http://reef.host:9944" \ No newline at end of file From bcde7000ddc76fc23329bf02105b7f091b5a5fb2 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:01:52 +0530 Subject: [PATCH 29/90] feat: bootnode update --- blueprints/reef-bootnode/docker-compose.yml | 59 +++++++++++++++------ blueprints/reef-bootnode/template.toml | 34 ++++++++---- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 45426bfb4..873948fdf 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -2,21 +2,50 @@ version: "3.9" services: reef-bootnode: - image: anukulpandey/reef-node:latest - platform: linux/amd64 - restart: unless-stopped - environment: - - P2P_PORT=${P2P_PORT} - - RPC_PORT=${RPC_PORT} - - NODE_NAME=${NODE_NAME} - - CHAIN_SPEC_URL=${CHAIN_SPEC_URL} + image: anukulpandey/reef-chain-node:linux-2 + container_name: reef-bootnode + working_dir: /workspace + volumes: - - ./bootnode-db:/reef-data - entrypoint: sh -c - command: > - "wget -qO /reef-data/customSpec.json $CHAIN_SPEC_URL && - reef-node build-spec --disable-default-bootnode --chain /reef-data/customSpec.json --raw > /reef-data/customSpecRaw.json && - reef-node --base-path /reef-data --chain /reef-data/customSpecRaw.json --port $P2P_PORT --rpc-port $RPC_PORT --no-telemetry --name $NODE_NAME --rpc-cors all --rpc-external" + - ./output:/output + ports: + - "${PORT}:${PORT}" - "${P2P_PORT}:${P2P_PORT}" - - "${RPC_PORT}:${RPC_PORT}" + + environment: + V1_ADDR: ${V1_ADDR} + V1_SEED: ${V1_SEED} + + V2_ADDR: ${V2_ADDR} + V2_SEED: ${V2_SEED} + + V3_ADDR: ${V3_ADDR} + V3_SEED: ${V3_SEED} + + PORT: ${PORT} + P2P_PORT: ${P2P_PORT} + + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📦 Installing dependencies...' && + apt-get update && + apt-get install -y python3 wget ca-certificates && + + echo '📥 Downloading bootstrap scripts...' && + mkdir -p /workspace && + wget -O /workspace/run.sh \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/run.sh && + wget -O /workspace/update-spec.py \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/update-spec.py && + wget -O /workspace/update-spec.sh \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/update-spec.sh && + + echo '🔧 Making scripts executable...' && + chmod +x /workspace/run.sh && + chmod +x /workspace/update-spec.sh && + + echo '🚀 Running spec generator + bootnode...' && + /workspace/run.sh" diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index 312075761..dff57e835 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -1,23 +1,37 @@ [variables] -NODE_NAME = "bootnode" -P2P_PORT = 30333 -RPC_PORT = 9944 -CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" +V1_ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +V2_ADDR = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" +V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" + +V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" +V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" + +PORT=8000 +P2P_PORT=30335 [[config.domains]] serviceName = "reef-bootnode" -port = 30333 +port = 30335 host = "${domain}" path = "/" [[config.domains]] serviceName = "reef-bootnode" -port = 9944 +port = 8000 host = "${domain}" path = "/" [config.env] -NODE_NAME = "bootnode" -P2P_PORT = 30333 -RPC_PORT = 9944 -CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" +V1_ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +V2_ADDR = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" +V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" + +V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" +V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" + +PORT=8000 +P2P_PORT=30335 \ No newline at end of file From ffad5d274e27270c7a290d8d5395ceafa6c5a5da Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:08:57 +0530 Subject: [PATCH 30/90] feat: spec generator running --- blueprints/reef-bootnode/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 873948fdf..d61176010 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -47,5 +47,5 @@ services: chmod +x /workspace/run.sh && chmod +x /workspace/update-spec.sh && - echo '🚀 Running spec generator + bootnode...' && + echo '🚀 Running spec generator + bootnode:' && /workspace/run.sh" From aa0d3ba0ad9c896218df60d6ad748550384be4fc Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:23:14 +0530 Subject: [PATCH 31/90] feat: validator --- blueprints/reef-validator/docker-compose.yml | 43 +++++++++++++ blueprints/reef-validator/reef.svg | 68 ++++++++++++++++++++ blueprints/reef-validator/template.toml | 39 +++++++++++ meta.json | 18 ++++++ 4 files changed, 168 insertions(+) create mode 100644 blueprints/reef-validator/docker-compose.yml create mode 100644 blueprints/reef-validator/reef.svg create mode 100644 blueprints/reef-validator/template.toml diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml new file mode 100644 index 000000000..39c407324 --- /dev/null +++ b/blueprints/reef-validator/docker-compose.yml @@ -0,0 +1,43 @@ +version: '3.8' + +services: + reef-validator: + image: anukulpandey/reef-chain-node + container_name: reef-validator + working_dir: /workspace + + ports: + - "${RPC_PORT}:${RPC_PORT}" + - "${P2P_PORT}:${P2P_PORT}" + + environment: + SPEC_URL: ${SPEC_URL} + BOOTNODE_NODE_KEY: ${BOOTNODE_NODE_KEY} + BOOTNODE_IP: ${BOOTNODE_IP} + V1SEED: ${V1SEED} + V1ADDR: ${V1ADDR} + RPC_PORT: ${RPC_PORT} + P2P_PORT: ${P2P_PORT} + VALIDATOR_NAME: ${VALIDATOR_NAME} + + volumes: + - ./output:/output + + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📦 Installing dependencies...' && + apt-get update && + apt-get install -y wget ca-certificates && + + echo '📥 Downloading start-validator.sh...' && + mkdir -p /workspace/scripts && + wget -O /workspace/scripts/start-validator.sh \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/validator/scripts/start-validator.sh && + + echo '🔧 Making script executable...' && + chmod +x /workspace/scripts/start-validator.sh && + + echo '🚀 Starting validator...' && + exec /workspace/scripts/start-validator.sh" diff --git a/blueprints/reef-validator/reef.svg b/blueprints/reef-validator/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-validator/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-validator/template.toml b/blueprints/reef-validator/template.toml new file mode 100644 index 000000000..597207dfe --- /dev/null +++ b/blueprints/reef-validator/template.toml @@ -0,0 +1,39 @@ +[variables] +V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" + +BOOTNODE_IP="72.60.35.83" + +VALIDATOR_NAME="Validator-1" + +RPC_PORT=9944 +P2P_PORT=30333 + +[[config.domains]] +serviceName = "reef-validator" +port = 30333 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-validator" +port = 9944 +host = "${domain}" +path = "/" + +[config.env] +V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" + +BOOTNODE_IP="72.60.35.83" + +VALIDATOR_NAME="Validator-1" + +RPC_PORT=9944 +P2P_PORT=30333 \ No newline at end of file diff --git a/meta.json b/meta.json index 6091e9ea7..6cf813755 100644 --- a/meta.json +++ b/meta.json @@ -4384,6 +4384,24 @@ "evm" ] }, + { + "id": "reef-rpc", + "name": "Reef Chain - Validator", + "version": "1", + "description": "Runs Validator which connects to bootnode for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "validator", + "self-hosted", + "reef-validator" + ] + }, { "id": "registry", "name": "Docker Registry", From 489f3937a33960fad7676a3737901d6a5a393073 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:24:35 +0530 Subject: [PATCH 32/90] fix: meta.json for validator --- meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta.json b/meta.json index 6cf813755..c730d7893 100644 --- a/meta.json +++ b/meta.json @@ -4385,7 +4385,7 @@ ] }, { - "id": "reef-rpc", + "id": "reef-validator", "name": "Reef Chain - Validator", "version": "1", "description": "Runs Validator which connects to bootnode for a custom Reef Chain cluster", From bf6d27c51542aed341191ae2fa12c0d4c1d38074 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:26:54 +0530 Subject: [PATCH 33/90] fix: exec binary for reef-validator --- blueprints/reef-validator/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index 39c407324..bbfa60db4 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: reef-validator: - image: anukulpandey/reef-chain-node + image: anukulpandey/reef-chain-node:linux-2 container_name: reef-validator working_dir: /workspace From b8d1e87e263e429618e89f38fe2b145bd2f63475 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:30:53 +0530 Subject: [PATCH 34/90] feat: validator start --- blueprints/reef-validator/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index bbfa60db4..740716471 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -39,5 +39,5 @@ services: echo '🔧 Making script executable...' && chmod +x /workspace/scripts/start-validator.sh && - echo '🚀 Starting validator...' && + echo '🚀 Starting validator $VALIDATOR_NAME' && exec /workspace/scripts/start-validator.sh" From 7b80d7a9440af97a7e35c968a9a3bd40d2b599f2 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:39:51 +0530 Subject: [PATCH 35/90] feat: remove container name --- blueprints/reef-validator/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index 740716471..05d6ba633 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -3,7 +3,6 @@ version: '3.8' services: reef-validator: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-validator working_dir: /workspace ports: From a0683dfcd4d61fe70e9258d1012e3311c4ca7f6a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 03:47:47 +0530 Subject: [PATCH 36/90] fix: env var for eth-rpc --- blueprints/reef-eth-rpc/template.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-eth-rpc/template.toml b/blueprints/reef-eth-rpc/template.toml index dfe5d0483..596ec115e 100644 --- a/blueprints/reef-eth-rpc/template.toml +++ b/blueprints/reef-eth-rpc/template.toml @@ -1,6 +1,6 @@ [variables] PORT = 8545 -NODE_RPC_URL = "http://reef.host:9944" +NODE_RPC_URL = "ws://reef.host:9944" [[config.domains]] serviceName = "reef-eth-rpc" @@ -10,4 +10,4 @@ path = "/" [config.env] PORT = 8545 -NODE_RPC_URL = "http://reef.host:9944" \ No newline at end of file +NODE_RPC_URL = "ws://reef.host:9944" \ No newline at end of file From 6e40b9a83faf04c977c65ac49e25f6392613bf06 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 16:13:13 +0530 Subject: [PATCH 37/90] feat: custom spec generator --- blueprints/reef-spec-gen/docker-compose.yml | 38 ++++++++++++ blueprints/reef-spec-gen/reef.svg | 68 +++++++++++++++++++++ blueprints/reef-spec-gen/template.toml | 9 +++ meta.json | 18 ++++++ 4 files changed, 133 insertions(+) create mode 100644 blueprints/reef-spec-gen/docker-compose.yml create mode 100644 blueprints/reef-spec-gen/reef.svg create mode 100644 blueprints/reef-spec-gen/template.toml diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml new file mode 100644 index 000000000..76292c0c4 --- /dev/null +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -0,0 +1,38 @@ +version: "3.9" + +services: + reef-spec-api: + image: anukulpandey/reef-chain-node + container_name: reef-spec-api + working_dir: /workspace + + volumes: + - ./output:/output + + ports: + - "8000:8000" + + entrypoint: /bin/sh + command: -c " + set -e && + apt-get update && + apt-get install -y python3 python3-pip wget ca-certificates jq && + + echo '📥 Downloading API + scripts...' && + mkdir -p /workspace/scripts && + + wget -O /workspace/scripts/update-spec.py \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/scripts/update-spec.py && + + wget -O /workspace/scripts/update-spec.sh \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/scripts/update-spec.sh && + + wget -O /workspace/api.py \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/api/api.py && + + chmod +x /workspace/scripts/update-spec.sh && + + pip3 install flask && + + echo '🚀 Starting Spec Generator API on port 8000...' && + python3 /workspace/api.py" diff --git a/blueprints/reef-spec-gen/reef.svg b/blueprints/reef-spec-gen/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-spec-gen/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-spec-gen/template.toml b/blueprints/reef-spec-gen/template.toml new file mode 100644 index 000000000..4df78b66e --- /dev/null +++ b/blueprints/reef-spec-gen/template.toml @@ -0,0 +1,9 @@ +[variables] + +[[config.domains]] +serviceName = "reef-spec-api" +port = 8000 +host = "${domain}" +path = "/" + +[config.env] diff --git a/meta.json b/meta.json index c730d7893..27d8d8fa3 100644 --- a/meta.json +++ b/meta.json @@ -4384,6 +4384,24 @@ "evm" ] }, + { + "id": "reef-spec-gen", + "name": "Reef Chain - Custom Spec Generator", + "version": "1", + "description": "Generates spec file for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "rpc", + "self-hosted", + "evm" + ] + }, { "id": "reef-validator", "name": "Reef Chain - Validator", From 9c4c14726c19851bc5e840bdacccc21eeae09cc8 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 16:20:40 +0530 Subject: [PATCH 38/90] fix: image for reef-spec --- blueprints/reef-spec-gen/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index 76292c0c4..be0c5d813 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.9" services: reef-spec-api: - image: anukulpandey/reef-chain-node + image: anukulpandey/reef-chain-node:linux-2 container_name: reef-spec-api working_dir: /workspace From 1ef6b3c78d717beee957135289575c6f1fdef8ba Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 18:16:51 +0530 Subject: [PATCH 39/90] feat: dev cluster --- .../reef-dev-cluster/docker-compose.yml | 36 ++++++++++ blueprints/reef-dev-cluster/reef.svg | 68 +++++++++++++++++++ blueprints/reef-dev-cluster/template.toml | 45 ++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 blueprints/reef-dev-cluster/docker-compose.yml create mode 100644 blueprints/reef-dev-cluster/reef.svg create mode 100644 blueprints/reef-dev-cluster/template.toml diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml new file mode 100644 index 000000000..e4a19fbc1 --- /dev/null +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -0,0 +1,36 @@ +services: + reef-dev-cluster: + image: anukulpandey/reef-chain-node:linux-2 + container_name: reef-run-local + working_dir: /workspace + + volumes: + - ./:/workspace + + ports: + - "30335:30335" + - "30333:30333" + - "30334:30334" + - "30336:30336" + - "9944:9944" + - "9945:9945" + - "9946:9946" + + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📥 Downloading Makefile...' && + wget -q -O /workspace/Makefile \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/reef-cluster/Makefile && + + echo '📦 Installing required tools...' && + apt-get update && + apt-get install -y make wget curl jq && + + echo '🔧 Preparing fake build output...' && + mkdir -p target/release && + ln -sf /usr/local/bin/reef-node target/release/reef-node && + + echo '🚀 Running make run-local...' && + make run-local" \ No newline at end of file diff --git a/blueprints/reef-dev-cluster/reef.svg b/blueprints/reef-dev-cluster/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-dev-cluster/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml new file mode 100644 index 000000000..8f52189a7 --- /dev/null +++ b/blueprints/reef-dev-cluster/template.toml @@ -0,0 +1,45 @@ +[variables] + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 30335 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 30334 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 30333 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 30336 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 9944 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 9945 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 9946 +host = "${domain}" +path = "/" + +[config.env] From 62d8e61d7c0482831b2603cedfdeb50948b72f3a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 18:18:56 +0530 Subject: [PATCH 40/90] fix: name --- blueprints/reef-dev-cluster/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index e4a19fbc1..c0e9b4d56 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -1,7 +1,7 @@ services: reef-dev-cluster: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-run-local + container_name: reef-dev-cluster working_dir: /workspace volumes: From 90eb1ae1bddd5fbfa37cdb0de23b398f072ecbcd Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 18:20:49 +0530 Subject: [PATCH 41/90] fix: meta.json --- meta.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/meta.json b/meta.json index 27d8d8fa3..828e8686b 100644 --- a/meta.json +++ b/meta.json @@ -4312,6 +4312,24 @@ "evm" ] }, + { + "id": "reef-dev-cluster", + "name": "Reef Chain - Dev Cluster", + "version": "1", + "description": "Development cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "dev-cluster", + "self-hosted", + "evm" + ] + }, { "id": "reef-eth-rpc", "name": "Reef Chain - ETH RPC", From 3e430b3430a0f294639afb45480ddff93e6e1482 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 11 Dec 2025 18:23:30 +0530 Subject: [PATCH 42/90] fix: wget --- blueprints/reef-dev-cluster/docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index c0e9b4d56..8539ed088 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -20,14 +20,14 @@ services: command: -c " set -e && - echo '📥 Downloading Makefile...' && - wget -q -O /workspace/Makefile \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/reef-cluster/Makefile && - echo '📦 Installing required tools...' && apt-get update && apt-get install -y make wget curl jq && + echo '📥 Downloading Makefile...' && + wget -q -O /workspace/Makefile \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/reef-cluster/Makefile && + echo '🔧 Preparing fake build output...' && mkdir -p target/release && ln -sf /usr/local/bin/reef-node target/release/reef-node && From 4af929dec0d2b173a3f54a0fd3bad7baa54624e2 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 12 Dec 2025 14:15:15 +0530 Subject: [PATCH 43/90] feat: reef-* removed --- blueprints/reef-faucet/docker-compose.yml | 21 ------- blueprints/reef-faucet/reef.svg | 68 ----------------------- blueprints/reef-faucet/template.toml | 19 ------- blueprints/reef-rpc/docker-compose.yml | 23 -------- blueprints/reef-rpc/reef.svg | 68 ----------------------- blueprints/reef-rpc/template.toml | 19 ------- meta.json | 36 ------------ 7 files changed, 254 deletions(-) delete mode 100644 blueprints/reef-faucet/docker-compose.yml delete mode 100644 blueprints/reef-faucet/reef.svg delete mode 100644 blueprints/reef-faucet/template.toml delete mode 100644 blueprints/reef-rpc/docker-compose.yml delete mode 100644 blueprints/reef-rpc/reef.svg delete mode 100644 blueprints/reef-rpc/template.toml diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml deleted file mode 100644 index 9045fe387..000000000 --- a/blueprints/reef-faucet/docker-compose.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3.9" - -services: - reef-faucet: - image: node:20-alpine - restart: unless-stopped - environment: - - MNEMONIC=${MNEMONIC} - - RPC_URL=${RPC_URL} - - TARGET_ADDRESS=${TARGET_ADDRESS} - - AMOUNT=${AMOUNT} - volumes: - - ./faucet-data:/faucet - working_dir: /faucet - entrypoint: sh -c - command: > - "apk add --no-cache curl git && - echo '>>> Downloading faucet.js...' && - curl -sL https://raw.githubusercontent.com/anukulpandey/reef-cluster-registration-script/main/faucet.js -o faucet.js && - echo '>>> Running faucet.js...' && - node faucet.js \"$MNEMONIC\" \"$RPC_URL\" \"$TARGET_ADDRESS\" ${AMOUNT:-1000}" \ No newline at end of file diff --git a/blueprints/reef-faucet/reef.svg b/blueprints/reef-faucet/reef.svg deleted file mode 100644 index 598d7930a..000000000 --- a/blueprints/reef-faucet/reef.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml deleted file mode 100644 index d253ddb71..000000000 --- a/blueprints/reef-faucet/template.toml +++ /dev/null @@ -1,19 +0,0 @@ -[variables] -NODE_NAME = "reef-faucet" -RPC_URL = "ws://127.0.0.1:9945" -MNEMONIC = "YOUR_MNEMONIC_HERE" -TARGET_ADDRESS = "TARGET_ADDRESS_HERE" -AMOUNT = 1000 - -[[config.domains]] -serviceName = "reef-faucet" -port = 9946 -host = "${domain}" -path = "/" - -[config.env] -NODE_NAME = "reef-faucet" -RPC_URL = "${RPC_URL}" -MNEMONIC = "${MNEMONIC}" -TARGET_ADDRESS = "${TARGET_ADDRESS}" -AMOUNT = "${AMOUNT}" \ No newline at end of file diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml deleted file mode 100644 index 59f0a8380..000000000 --- a/blueprints/reef-rpc/docker-compose.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: "3.9" - -services: - reef-rpc: - image: anukulpandey/reef-node:latest - platform: linux/amd64 - restart: unless-stopped - environment: - - P2P_PORT=${P2P_PORT} - - RPC_PORT=${RPC_PORT} - - NODE_NAME=${NODE_NAME} - - CHAIN_SPEC_URL=${CHAIN_SPEC_URL} - - BOOTNODE_ADDR=${BOOTNODE_ADDR} - volumes: - - ./rpc-db:/reef-data - entrypoint: sh -c - command: > - "wget -qO /reef-data/customSpec.json $CHAIN_SPEC_URL && - reef-node build-spec --disable-default-bootnode --chain /reef-data/customSpec.json --raw > /reef-data/customSpecRaw.json && - reef-node --base-path /reef-data --chain /reef-data/customSpecRaw.json --port $P2P_PORT --rpc-port $RPC_PORT --no-telemetry --name $NODE_NAME --rpc-cors all --rpc-external --rpc-methods Unsafe --rpc-max-connections 10000 --pruning=archive --bootnodes $BOOTNODE_ADDR" - ports: - - "${P2P_PORT}:${P2P_PORT}" - - "${RPC_PORT}:${RPC_PORT}" diff --git a/blueprints/reef-rpc/reef.svg b/blueprints/reef-rpc/reef.svg deleted file mode 100644 index 598d7930a..000000000 --- a/blueprints/reef-rpc/reef.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml deleted file mode 100644 index 311e92634..000000000 --- a/blueprints/reef-rpc/template.toml +++ /dev/null @@ -1,19 +0,0 @@ -[variables] -NODE_NAME = "rpc-node" -P2P_PORT = 30334 -RPC_PORT = 9945 -CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" -BOOTNODE_ADDR = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" - -[[config.domains]] -serviceName = "reef-rpc" -port = 9945 -host = "${domain}" -path = "/" - -[config.env] -NODE_NAME = "rpc-node" -P2P_PORT = 30334 -RPC_PORT = 9945 -CHAIN_SPEC_URL = "https://raw.githubusercontent.com/anukulpandey/reef-pelagia-testnet-customSpec.json/main/customSpec.json" -BOOTNODE_ADDR = "/ip4/72.60.35.83/tcp/30933/p2p/12D3KooWFUtxzdPEziCEHCNHpVNLPyuVKhKfVoEHyiGZWktJA4ES" diff --git a/meta.json b/meta.json index 828e8686b..7e4f7ab09 100644 --- a/meta.json +++ b/meta.json @@ -4348,24 +4348,6 @@ "keys" ] }, - { - "id": "reef-faucet", - "name": "Reef Chain - Cluster Faucet", - "version": "1", - "description": "Faucet service for a custom Reef Chain cluster", - "links": { - "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", - "docs": "https://docs.reef.io/" - }, - "logo": "reef.svg", - "tags": [ - "reef-chain", - "faucet", - "self-hosted", - "native" - ] - }, { "id": "reef-keygen", "name": "Reef Chain - Keys Generator", @@ -4384,24 +4366,6 @@ "keys" ] }, - { - "id": "reef-rpc", - "name": "Reef Chain - RPC", - "version": "1", - "description": "Runs RPC for a custom Reef Chain cluster", - "links": { - "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", - "docs": "https://docs.reef.io/" - }, - "logo": "reef.svg", - "tags": [ - "reef-chain", - "rpc", - "self-hosted", - "evm" - ] - }, { "id": "reef-spec-gen", "name": "Reef Chain - Custom Spec Generator", From 7045f5e661627cbdc2de09b88a8a917f37a42be2 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 12 Dec 2025 17:22:51 +0530 Subject: [PATCH 44/90] feat: reef-dev cluster --- blueprints/reef-dev-cluster/docker-compose.yml | 15 ++++++++++++--- blueprints/reef-dev-cluster/template.toml | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 8539ed088..27f5b8682 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -4,6 +4,15 @@ services: container_name: reef-dev-cluster working_dir: /workspace + environment: + v1addr: "${v1addr}" + v2addr: "${v2addr}" + v3addr: "${v3addr}" + v1sec: "${v1sec}" + v2sec: "${v2sec}" + v3sec: "${v3sec}" + specgenurl: "${specgenurl}" + volumes: - ./:/workspace @@ -28,9 +37,9 @@ services: wget -q -O /workspace/Makefile \ https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/reef-cluster/Makefile && - echo '🔧 Preparing fake build output...' && + echo '🔧 Preparing build output...' && mkdir -p target/release && ln -sf /usr/local/bin/reef-node target/release/reef-node && - echo '🚀 Running make run-local...' && - make run-local" \ No newline at end of file + echo '🚀 Starting local network...' && + make run-local" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index 8f52189a7..eb5a1d225 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -1,4 +1,13 @@ [variables] +v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" +v3sec = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" + +v1addr = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +v2addr = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" +v3addr = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" + +specgenurl = "http://reef.host:8000" [[config.domains]] serviceName = "reef-dev-cluster" @@ -43,3 +52,12 @@ host = "${domain}" path = "/" [config.env] +v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" +v3sec = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" + +v1addr = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +v2addr = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" +v3addr = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" + +specgenurl = "http://reef.host:8000" \ No newline at end of file From 6bbeec38557112b17edfcc762efbcadc2e267ee9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:44:00 -0600 Subject: [PATCH 45/90] Update docker-compose.yml --- blueprints/reef-bootnode/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index d61176010..c0e5a539f 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -1,5 +1,4 @@ version: "3.9" - services: reef-bootnode: image: anukulpandey/reef-chain-node:linux-2 From 593d3d29a676006eb1ec42e24257bddbd5287860 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 12:27:36 +0530 Subject: [PATCH 46/90] feat: close rpc ports for reef-dev-cluster --- blueprints/reef-dev-cluster/docker-compose.yml | 3 --- blueprints/reef-dev-cluster/template.toml | 18 ------------------ 2 files changed, 21 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 27f5b8682..7890291ce 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -21,9 +21,6 @@ services: - "30333:30333" - "30334:30334" - "30336:30336" - - "9944:9944" - - "9945:9945" - - "9946:9946" entrypoint: /bin/sh command: -c " diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index eb5a1d225..126b5aee1 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -33,24 +33,6 @@ port = 30336 host = "${domain}" path = "/" -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 9944 -host = "${domain}" -path = "/" - -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 9945 -host = "${domain}" -path = "/" - -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 9946 -host = "${domain}" -path = "/" - [config.env] v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" From 4f3ed19a7aa25bd69b9b2354446720f9129cc728 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 12:44:11 +0530 Subject: [PATCH 47/90] feat: removed rpc ports from validator --- blueprints/reef-validator/docker-compose.yml | 2 -- blueprints/reef-validator/template.toml | 8 -------- 2 files changed, 10 deletions(-) diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index 05d6ba633..ce817affb 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -6,7 +6,6 @@ services: working_dir: /workspace ports: - - "${RPC_PORT}:${RPC_PORT}" - "${P2P_PORT}:${P2P_PORT}" environment: @@ -15,7 +14,6 @@ services: BOOTNODE_IP: ${BOOTNODE_IP} V1SEED: ${V1SEED} V1ADDR: ${V1ADDR} - RPC_PORT: ${RPC_PORT} P2P_PORT: ${P2P_PORT} VALIDATOR_NAME: ${VALIDATOR_NAME} diff --git a/blueprints/reef-validator/template.toml b/blueprints/reef-validator/template.toml index 597207dfe..1ddcd47a4 100644 --- a/blueprints/reef-validator/template.toml +++ b/blueprints/reef-validator/template.toml @@ -9,7 +9,6 @@ BOOTNODE_IP="72.60.35.83" VALIDATOR_NAME="Validator-1" -RPC_PORT=9944 P2P_PORT=30333 [[config.domains]] @@ -18,12 +17,6 @@ port = 30333 host = "${domain}" path = "/" -[[config.domains]] -serviceName = "reef-validator" -port = 9944 -host = "${domain}" -path = "/" - [config.env] V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" @@ -35,5 +28,4 @@ BOOTNODE_IP="72.60.35.83" VALIDATOR_NAME="Validator-1" -RPC_PORT=9944 P2P_PORT=30333 \ No newline at end of file From 12847f421fd9724c122d2fa7ccb450864e2e188b Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:11:16 +0530 Subject: [PATCH 48/90] feat: rpc node seperate --- .../reef-dev-cluster/docker-compose.yml | 3 +- blueprints/reef-rpc/docker-compose.yml | 39 +++++++++++ blueprints/reef-rpc/reef.svg | 68 +++++++++++++++++++ blueprints/reef-rpc/template.toml | 31 +++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 blueprints/reef-rpc/docker-compose.yml create mode 100644 blueprints/reef-rpc/reef.svg create mode 100644 blueprints/reef-rpc/template.toml diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 7890291ce..513616bc0 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -21,6 +21,7 @@ services: - "30333:30333" - "30334:30334" - "30336:30336" + - "8001:8001" entrypoint: /bin/sh command: -c " @@ -28,7 +29,7 @@ services: echo '📦 Installing required tools...' && apt-get update && - apt-get install -y make wget curl jq && + apt-get install -y make wget curl jq python3 && echo '📥 Downloading Makefile...' && wget -q -O /workspace/Makefile \ diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml new file mode 100644 index 000000000..bba9aac1b --- /dev/null +++ b/blueprints/reef-rpc/docker-compose.yml @@ -0,0 +1,39 @@ +version: '3.8' + +services: + reef-rpc: + image: anukulpandey/reef-chain-node:linux-2 + working_dir: /workspace + + ports: + - "${P2P_PORT}:${P2P_PORT}" + - "${RPC_PORT}:${RPC_PORT}" + + environment: + SPEC_URL: ${SPEC_URL} + BOOTNODE_NODE_KEY: ${BOOTNODE_NODE_KEY} + BOOTNODE_IP: ${BOOTNODE_IP} + P2P_PORT: ${P2P_PORT} + RPC_PORT: ${RPC_PORT} + + volumes: + - ./output:/output + + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📦 Installing dependencies...' && + apt-get update && + apt-get install -y wget ca-certificates && + + echo '📥 Downloading start-validator.sh...' && + mkdir -p /workspace/scripts && + wget -O /workspace/scripts/start-validator.sh \ + https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/rpc/scripts/start-rpc.sh && + + echo '🔧 Making script executable...' && + chmod +x /workspace/scripts/start-rpc.sh && + + echo '🚀 Starting RPC' && + exec /workspace/scripts/start-rpc.sh" diff --git a/blueprints/reef-rpc/reef.svg b/blueprints/reef-rpc/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-rpc/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml new file mode 100644 index 000000000..1ddcd47a4 --- /dev/null +++ b/blueprints/reef-rpc/template.toml @@ -0,0 +1,31 @@ +[variables] +V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" + +BOOTNODE_IP="72.60.35.83" + +VALIDATOR_NAME="Validator-1" + +P2P_PORT=30333 + +[[config.domains]] +serviceName = "reef-validator" +port = 30333 +host = "${domain}" +path = "/" + +[config.env] +V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" +V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" + +SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" + +BOOTNODE_IP="72.60.35.83" + +VALIDATOR_NAME="Validator-1" + +P2P_PORT=30333 \ No newline at end of file From 0117938d14fe4edc7cfec70bb5b825b3348e05b0 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:11:57 +0530 Subject: [PATCH 49/90] feat: rpc node --- meta.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/meta.json b/meta.json index 7e4f7ab09..e4706db22 100644 --- a/meta.json +++ b/meta.json @@ -4384,6 +4384,24 @@ "evm" ] }, + { + "id": "reef-rpc", + "name": "Reef Chain - RPC Node", + "version": "1", + "description": "Runs RPC which connects to bootnode for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "validator", + "self-hosted", + "reef-validator" + ] + }, { "id": "reef-validator", "name": "Reef Chain - Validator", From 93c6307831c67cb287df497e1f997deb436083be Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:15:10 +0530 Subject: [PATCH 50/90] feat: reef-rpc --- blueprints/reef-rpc/template.toml | 32 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index 1ddcd47a4..08bad34fe 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -1,31 +1,29 @@ [variables] -V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" - -SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" +SPEC_URL = "http://reef.host:8001/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" -VALIDATOR_NAME="Validator-1" +P2P_PORT=30339 +RPC_PORT=9994 -P2P_PORT=30333 +[[config.domains]] +serviceName = "reef-rpc" +port = 30339 +host = "${domain}" +path = "/" [[config.domains]] -serviceName = "reef-validator" -port = 30333 +serviceName = "reef-rpc" +port = 9994 host = "${domain}" path = "/" [config.env] -V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" - -SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" +SPEC_URL = "http://reef.host:8001/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" -VALIDATOR_NAME="Validator-1" - -P2P_PORT=30333 \ No newline at end of file +P2P_PORT=30339 +RPC_PORT=9994 \ No newline at end of file From 5ba53205d42b14ced4eb90c11a4c188e906007df Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:17:02 +0530 Subject: [PATCH 51/90] fix: meta --- meta.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/meta.json b/meta.json index e4706db22..3e7aa3cfd 100644 --- a/meta.json +++ b/meta.json @@ -4367,10 +4367,10 @@ ] }, { - "id": "reef-spec-gen", - "name": "Reef Chain - Custom Spec Generator", + "id": "reef-rpc", + "name": "Reef Chain - RPC Node", "version": "1", - "description": "Generates spec file for a custom Reef Chain cluster", + "description": "Runs RPC which connects to bootnode for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", @@ -4379,16 +4379,16 @@ "logo": "reef.svg", "tags": [ "reef-chain", - "rpc", + "validator", "self-hosted", - "evm" + "reef-validator" ] }, { - "id": "reef-rpc", - "name": "Reef Chain - RPC Node", + "id": "reef-spec-gen", + "name": "Reef Chain - Custom Spec Generator", "version": "1", - "description": "Runs RPC which connects to bootnode for a custom Reef Chain cluster", + "description": "Generates spec file for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", @@ -4397,9 +4397,9 @@ "logo": "reef.svg", "tags": [ "reef-chain", - "validator", + "rpc", "self-hosted", - "reef-validator" + "evm" ] }, { From 49fcc4ef5fe231374e3ea8095c4d762e8f5b703d Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:21:42 +0530 Subject: [PATCH 52/90] feat: removed version from docker-compose bootstrap --- blueprints/reef-bootnode/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index c0e5a539f..183db2c16 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.9" services: reef-bootnode: image: anukulpandey/reef-chain-node:linux-2 From 67828bf60a6af73994a6b4eb44d398993368b91c Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:35:49 +0530 Subject: [PATCH 53/90] feat: expose 9944 for dev cluster --- blueprints/reef-dev-cluster/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 513616bc0..131e5afec 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -22,6 +22,7 @@ services: - "30334:30334" - "30336:30336" - "8001:8001" + - "9944:9944" entrypoint: /bin/sh command: -c " From 4de4fcbd5f7e5d541247d506cd264f09ef88125c Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Mon, 15 Dec 2025 15:37:48 +0530 Subject: [PATCH 54/90] fix: start-rpc script --- blueprints/reef-rpc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index bba9aac1b..c17a80b05 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -29,7 +29,7 @@ services: echo '📥 Downloading start-validator.sh...' && mkdir -p /workspace/scripts && - wget -O /workspace/scripts/start-validator.sh \ + wget -O /workspace/scripts/start-rpc.sh \ https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/rpc/scripts/start-rpc.sh && echo '🔧 Making script executable...' && From 4cdff528573e7341a9245bffcb2cbed910ba09f0 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 18 Dec 2025 18:12:06 +0530 Subject: [PATCH 55/90] feat: eth-rpc rpc host --- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index 9cb09b896..8f809aaaf 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -11,4 +11,4 @@ services: ports: - "${PORT}:${PORT}" entrypoint: /bin/sh - command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT" + command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-host 0.0.0.0" From 0afec758afa1426479a5586b42f17df93c383eee Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 18 Dec 2025 18:15:08 +0530 Subject: [PATCH 56/90] fix: flag for eth-rpc external rpc connections --- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index 8f809aaaf..3c5957f72 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -11,4 +11,4 @@ services: ports: - "${PORT}:${PORT}" entrypoint: /bin/sh - command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-host 0.0.0.0" + command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-external" From 1d42a67a899432668eba6badc987ed7874166a93 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 18 Dec 2025 18:19:39 +0530 Subject: [PATCH 57/90] feat: cors for all --- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index 3c5957f72..6ca2f8730 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -11,4 +11,4 @@ services: ports: - "${PORT}:${PORT}" entrypoint: /bin/sh - command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-external" + command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-external --rpc-cors all" From c296fa03c2241bd18c6f3f89e5985415a32e25d4 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:28:59 +0530 Subject: [PATCH 58/90] fix: meta.json missing ] --- meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta.json b/meta.json index f96a449d4..afa596734 100644 --- a/meta.json +++ b/meta.json @@ -5098,7 +5098,7 @@ "resume", "cv", "productivity", - "document" + "document"] }, { "id": "reef-bootnode", From 05ea5ba6291d280f5860b6db697bf846b6c2657c Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:32:24 +0530 Subject: [PATCH 59/90] feat:process meta --- meta.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meta.json b/meta.json index afa596734..4ac3cbafb 100644 --- a/meta.json +++ b/meta.json @@ -5098,7 +5098,8 @@ "resume", "cv", "productivity", - "document"] + "document" + ] }, { "id": "reef-bootnode", From 5beccb1a2061d6c7d68478b03182e33965412bca Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:38:52 +0530 Subject: [PATCH 60/90] feat: removed container_name from docker-compose --- blueprints/reef-bootnode/docker-compose.yml | 1 - blueprints/reef-dev-cluster/docker-compose.yml | 1 - blueprints/reef-eth-rpc/docker-compose.yml | 1 - blueprints/reef-keygen/docker-compose.yml | 1 - blueprints/reef-spec-gen/docker-compose.yml | 1 - 5 files changed, 5 deletions(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 183db2c16..85cae706e 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -1,7 +1,6 @@ services: reef-bootnode: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-bootnode working_dir: /workspace volumes: diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 131e5afec..d385b6cea 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -1,7 +1,6 @@ services: reef-dev-cluster: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-dev-cluster working_dir: /workspace environment: diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index 6ca2f8730..888617044 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -1,7 +1,6 @@ services: reef-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:linux - container_name: reef-eth-rpc working_dir: /workspace volumes: - ./output:/output diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index ff5afc58e..d23729482 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,7 +1,6 @@ services: reef-keygen: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-keygen working_dir: /workspace volumes: - ./output:/output diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index be0c5d813..7ff9a58a2 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -3,7 +3,6 @@ version: "3.9" services: reef-spec-api: image: anukulpandey/reef-chain-node:linux-2 - container_name: reef-spec-api working_dir: /workspace volumes: From 14d369fae562d96aabb586e2357490e7dde63574 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:41:12 +0530 Subject: [PATCH 61/90] feat: port and p2p port must be string for bootnode --- blueprints/reef-bootnode/template.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index dff57e835..afce0eb70 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -8,8 +8,8 @@ V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" -PORT=8000 -P2P_PORT=30335 +PORT="8000" +P2P_PORT="30335" [[config.domains]] serviceName = "reef-bootnode" @@ -33,5 +33,5 @@ V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" -PORT=8000 -P2P_PORT=30335 \ No newline at end of file +PORT="8000" +P2P_PORT="30335" \ No newline at end of file From d0fed69197e3b489de35a8c1a7fc753fd46bd41a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:42:23 +0530 Subject: [PATCH 62/90] feat: ports for reef dev cluster --- blueprints/reef-dev-cluster/docker-compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index d385b6cea..4bff67e5f 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -16,12 +16,12 @@ services: - ./:/workspace ports: - - "30335:30335" - - "30333:30333" - - "30334:30334" - - "30336:30336" - - "8001:8001" - - "9944:9944" + - "30335" + - "30333" + - "30334" + - "30336" + - "8001" + - "9944" entrypoint: /bin/sh command: -c " From e9916775d3a6dec1fab947425a4c8ad44feb9a89 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:43:00 +0530 Subject: [PATCH 63/90] fix: ports for reef-dev-cluster --- blueprints/reef-dev-cluster/template.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index 126b5aee1..6f4abaeb9 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -33,6 +33,18 @@ port = 30336 host = "${domain}" path = "/" +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8001 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 9944 +host = "${domain}" +path = "/" + [config.env] v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" From 7469a84ee6c6db896399186882fc44bcd02fe9e7 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 26 Dec 2025 13:45:26 +0530 Subject: [PATCH 64/90] feat: all ports to strings --- blueprints/reef-eth-rpc/template.toml | 4 ++-- blueprints/reef-keygen/template.toml | 4 ++-- blueprints/reef-rpc/template.toml | 8 ++++---- blueprints/reef-spec-gen/docker-compose.yml | 2 +- blueprints/reef-validator/template.toml | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/blueprints/reef-eth-rpc/template.toml b/blueprints/reef-eth-rpc/template.toml index 596ec115e..15b02a3c3 100644 --- a/blueprints/reef-eth-rpc/template.toml +++ b/blueprints/reef-eth-rpc/template.toml @@ -1,5 +1,5 @@ [variables] -PORT = 8545 +PORT = "8545" NODE_RPC_URL = "ws://reef.host:9944" [[config.domains]] @@ -9,5 +9,5 @@ host = "${domain}" path = "/" [config.env] -PORT = 8545 +PORT = "8545" NODE_RPC_URL = "ws://reef.host:9944" \ No newline at end of file diff --git a/blueprints/reef-keygen/template.toml b/blueprints/reef-keygen/template.toml index 4fbd545e5..b8cde3e76 100644 --- a/blueprints/reef-keygen/template.toml +++ b/blueprints/reef-keygen/template.toml @@ -1,5 +1,5 @@ [variables] -PORT = 48765 +PORT = "48765" [[config.domains]] serviceName = "reef-keygen" @@ -8,4 +8,4 @@ host = "${domain}" path = "/" [config.env] -PORT = 48765 \ No newline at end of file +PORT = "48765" \ No newline at end of file diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index 08bad34fe..c87ec8ade 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -4,8 +4,8 @@ BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" -P2P_PORT=30339 -RPC_PORT=9994 +P2P_PORT="30339" +RPC_PORT="9994" [[config.domains]] serviceName = "reef-rpc" @@ -25,5 +25,5 @@ BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" -P2P_PORT=30339 -RPC_PORT=9994 \ No newline at end of file +P2P_PORT="30339" +RPC_PORT="9994" \ No newline at end of file diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index 7ff9a58a2..7ebc1d31a 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -9,7 +9,7 @@ services: - ./output:/output ports: - - "8000:8000" + - "8000" entrypoint: /bin/sh command: -c " diff --git a/blueprints/reef-validator/template.toml b/blueprints/reef-validator/template.toml index 1ddcd47a4..f64b18640 100644 --- a/blueprints/reef-validator/template.toml +++ b/blueprints/reef-validator/template.toml @@ -9,7 +9,7 @@ BOOTNODE_IP="72.60.35.83" VALIDATOR_NAME="Validator-1" -P2P_PORT=30333 +P2P_PORT="30333" [[config.domains]] serviceName = "reef-validator" @@ -28,4 +28,4 @@ BOOTNODE_IP="72.60.35.83" VALIDATOR_NAME="Validator-1" -P2P_PORT=30333 \ No newline at end of file +P2P_PORT="30333" \ No newline at end of file From 819e26d63ed91f1c1465b45ca1eb5cfd63c44644 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Wed, 8 Apr 2026 13:26:20 +0530 Subject: [PATCH 65/90] Wire Reef templates to external scripts repo --- blueprints/reef-bootnode/docker-compose.yml | 20 +++--- .../reef-dev-cluster/docker-compose.yml | 17 ++--- blueprints/reef-dev-cluster/template.toml | 8 ++- blueprints/reef-faucet/docker-compose.yml | 40 +++++++++++ blueprints/reef-faucet/reef.svg | 68 +++++++++++++++++++ blueprints/reef-faucet/template.toml | 21 ++++++ blueprints/reef-rpc/docker-compose.yml | 12 ++-- blueprints/reef-spec-gen/docker-compose.yml | 8 +-- blueprints/reef-validator/docker-compose.yml | 10 +-- meta.json | 32 +++++++-- 10 files changed, 195 insertions(+), 41 deletions(-) create mode 100644 blueprints/reef-faucet/docker-compose.yml create mode 100644 blueprints/reef-faucet/reef.svg create mode 100644 blueprints/reef-faucet/template.toml diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 85cae706e..42644b701 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -32,17 +32,17 @@ services: apt-get install -y python3 wget ca-certificates && echo '📥 Downloading bootstrap scripts...' && - mkdir -p /workspace && - wget -O /workspace/run.sh \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/run.sh && - wget -O /workspace/update-spec.py \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/update-spec.py && - wget -O /workspace/update-spec.sh \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/bootstrap/scripts/update-spec.sh && + mkdir -p /workspace/bootstrap/scripts && + wget -O /workspace/bootstrap/scripts/run.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/run.sh && + wget -O /workspace/bootstrap/scripts/update-spec.py \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/update-spec.py && + wget -O /workspace/bootstrap/scripts/update-spec.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/update-spec.sh && echo '🔧 Making scripts executable...' && - chmod +x /workspace/run.sh && - chmod +x /workspace/update-spec.sh && + chmod +x /workspace/bootstrap/scripts/run.sh && + chmod +x /workspace/bootstrap/scripts/update-spec.sh && echo '🚀 Running spec generator + bootnode:' && - /workspace/run.sh" + exec /workspace/bootstrap/scripts/run.sh" diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 4bff67e5f..d2ba72974 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -22,6 +22,7 @@ services: - "30336" - "8001" - "9944" + - "8545" entrypoint: /bin/sh command: -c " @@ -29,15 +30,15 @@ services: echo '📦 Installing required tools...' && apt-get update && - apt-get install -y make wget curl jq python3 && + apt-get install -y wget curl python3 ca-certificates && - echo '📥 Downloading Makefile...' && - wget -q -O /workspace/Makefile \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/reef-cluster/Makefile && + echo '📥 Downloading cluster launcher...' && + mkdir -p /workspace/dev-cluster && + wget -q -O /workspace/dev-cluster/start-cluster.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/dev-cluster/start-cluster.sh && - echo '🔧 Preparing build output...' && - mkdir -p target/release && - ln -sf /usr/local/bin/reef-node target/release/reef-node && + echo '🔧 Making script executable...' && + chmod +x /workspace/dev-cluster/start-cluster.sh && echo '🚀 Starting local network...' && - make run-local" + exec /workspace/dev-cluster/start-cluster.sh" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index 6f4abaeb9..a8f341dcd 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -45,6 +45,12 @@ port = 9944 host = "${domain}" path = "/" +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8545 +host = "${domain}" +path = "/" + [config.env] v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" @@ -54,4 +60,4 @@ v1addr = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" v2addr = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" v3addr = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" -specgenurl = "http://reef.host:8000" \ No newline at end of file +specgenurl = "http://reef.host:8000" diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml new file mode 100644 index 000000000..6a3c06f08 --- /dev/null +++ b/blueprints/reef-faucet/docker-compose.yml @@ -0,0 +1,40 @@ +services: + reef-faucet: + image: anukulpandey/reef-chain-node:linux-2 + working_dir: /workspace + + ports: + - "${PORT}:${PORT}" + + environment: + WS_ENDPOINT: ${WS_ENDPOINT} + EVM_RPC_URL: ${EVM_RPC_URL} + FAUCET_SEED: ${FAUCET_SEED} + PORT: ${PORT} + DEFAULT_AMOUNT: ${DEFAULT_AMOUNT} + MAX_AMOUNT: ${MAX_AMOUNT} + + volumes: + - ./output:/output + + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📦 Installing dependencies...' && + apt-get update && + apt-get install -y nodejs npm wget ca-certificates && + + echo '📥 Downloading faucet sources...' && + mkdir -p /workspace/faucet && + wget -O /workspace/faucet/package.json \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/faucet/package.json && + wget -O /workspace/faucet/server.js \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/faucet/server.js && + + echo '📦 Installing faucet npm dependencies...' && + cd /workspace/faucet && + npm install --omit=dev && + + echo '🚰 Starting Reef faucet on port $PORT' && + exec npm start" diff --git a/blueprints/reef-faucet/reef.svg b/blueprints/reef-faucet/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-faucet/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml new file mode 100644 index 000000000..7b3aee925 --- /dev/null +++ b/blueprints/reef-faucet/template.toml @@ -0,0 +1,21 @@ +[variables] +PORT = "8080" +WS_ENDPOINT = "ws://reef.host:9944" +EVM_RPC_URL = "http://reef.host:8545" +FAUCET_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +DEFAULT_AMOUNT = "1" +MAX_AMOUNT = "10" + +[[config.domains]] +serviceName = "reef-faucet" +port = 8080 +host = "${domain}" +path = "/" + +[config.env] +PORT = "8080" +WS_ENDPOINT = "ws://reef.host:9944" +EVM_RPC_URL = "http://reef.host:8545" +FAUCET_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +DEFAULT_AMOUNT = "1" +MAX_AMOUNT = "10" diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index c17a80b05..214b57160 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -27,13 +27,13 @@ services: apt-get update && apt-get install -y wget ca-certificates && - echo '📥 Downloading start-validator.sh...' && - mkdir -p /workspace/scripts && - wget -O /workspace/scripts/start-rpc.sh \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/rpc/scripts/start-rpc.sh && + echo '📥 Downloading start-rpc.sh...' && + mkdir -p /workspace/rpc/scripts && + wget -O /workspace/rpc/scripts/start-rpc.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/rpc/scripts/start-rpc.sh && echo '🔧 Making script executable...' && - chmod +x /workspace/scripts/start-rpc.sh && + chmod +x /workspace/rpc/scripts/start-rpc.sh && echo '🚀 Starting RPC' && - exec /workspace/scripts/start-rpc.sh" + exec /workspace/rpc/scripts/start-rpc.sh" diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index 7ebc1d31a..5c0c571b3 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -15,19 +15,19 @@ services: command: -c " set -e && apt-get update && - apt-get install -y python3 python3-pip wget ca-certificates jq && + apt-get install -y python3 python3-pip wget ca-certificates && echo '📥 Downloading API + scripts...' && mkdir -p /workspace/scripts && wget -O /workspace/scripts/update-spec.py \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/scripts/update-spec.py && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/scripts/update-spec.py && wget -O /workspace/scripts/update-spec.sh \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/scripts/update-spec.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/scripts/update-spec.sh && wget -O /workspace/api.py \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/spec-generator/api/api.py && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/api/api.py && chmod +x /workspace/scripts/update-spec.sh && diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index ce817affb..6ea4805ca 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -29,12 +29,12 @@ services: apt-get install -y wget ca-certificates && echo '📥 Downloading start-validator.sh...' && - mkdir -p /workspace/scripts && - wget -O /workspace/scripts/start-validator.sh \ - https://raw.githubusercontent.com/anukulpandey/reef-dokploy-templates-v2/refs/heads/master/validator/scripts/start-validator.sh && + mkdir -p /workspace/validator/scripts && + wget -O /workspace/validator/scripts/start-validator.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/validator/scripts/start-validator.sh && echo '🔧 Making script executable...' && - chmod +x /workspace/scripts/start-validator.sh && + chmod +x /workspace/validator/scripts/start-validator.sh && echo '🚀 Starting validator $VALIDATOR_NAME' && - exec /workspace/scripts/start-validator.sh" + exec /workspace/validator/scripts/start-validator.sh" diff --git a/meta.json b/meta.json index 4ac3cbafb..4388bb553 100644 --- a/meta.json +++ b/meta.json @@ -5108,7 +5108,7 @@ "description": "Runs bootnode for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5126,7 +5126,7 @@ "description": "Development cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5144,7 +5144,7 @@ "description": "EVM layer rpc for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-eth-rpc", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5162,7 +5162,7 @@ "description": "Generates 3 Validators keys for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5180,7 +5180,7 @@ "description": "Runs RPC which connects to bootnode for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5198,7 +5198,7 @@ "description": "Generates spec file for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5216,7 +5216,7 @@ "description": "Runs Validator which connects to bootnode for a custom Reef Chain cluster", "links": { "github": "https://github.com/reef-chain/chain-upgrade", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-node", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", "docs": "https://docs.reef.io/" }, "logo": "reef.svg", @@ -5227,6 +5227,24 @@ "reef-validator" ] }, + { + "id": "reef-faucet", + "name": "Reef Chain - Faucet", + "version": "1", + "description": "HTTP faucet for sending native Reef funds to EVM addresses on a custom Reef Chain cluster", + "links": { + "github": "https://github.com/anukulpandey/dokploy-reef-chain-scripts", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "faucet", + "self-hosted", + "evm" + ] + }, { "id": "registry", "name": "Docker Registry", From 17f8f451031870f5285702fe9c5310b034ffb3d1 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 17:07:35 +0530 Subject: [PATCH 66/90] fix: align reef templates with dokploy rules --- blueprints/reef-bootnode/docker-compose.yml | 15 ++++++----- blueprints/reef-bootnode/template.toml | 26 +++++++++---------- .../reef-dev-cluster/docker-compose.yml | 7 +++-- blueprints/reef-dev-cluster/template.toml | 24 ++++++++--------- blueprints/reef-eth-rpc/docker-compose.yml | 7 +++-- blueprints/reef-faucet/docker-compose.yml | 11 +++++--- blueprints/reef-faucet/template.toml | 4 +-- blueprints/reef-keygen/docker-compose.yml | 7 +++-- blueprints/reef-rpc/docker-compose.yml | 11 ++++---- blueprints/reef-spec-gen/docker-compose.yml | 15 ++++++----- blueprints/reef-spec-gen/template.toml | 2 +- blueprints/reef-validator/docker-compose.yml | 9 ++++--- blueprints/reef-validator/template.toml | 6 ++--- 13 files changed, 81 insertions(+), 63 deletions(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 42644b701..80efc779c 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -1,14 +1,17 @@ +version: "3.8" + services: reef-bootnode: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace volumes: - ./output:/output - ports: - - "${PORT}:${PORT}" - - "${P2P_PORT}:${P2P_PORT}" + expose: + - "${PORT}" + - "${P2P_PORT}" environment: V1_ADDR: ${V1_ADDR} @@ -34,11 +37,11 @@ services: echo '📥 Downloading bootstrap scripts...' && mkdir -p /workspace/bootstrap/scripts && wget -O /workspace/bootstrap/scripts/run.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/run.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/bootstrap/scripts/run.sh && wget -O /workspace/bootstrap/scripts/update-spec.py \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/update-spec.py && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/bootstrap/scripts/update-spec.py && wget -O /workspace/bootstrap/scripts/update-spec.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/bootstrap/scripts/update-spec.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/bootstrap/scripts/update-spec.sh && echo '🔧 Making scripts executable...' && chmod +x /workspace/bootstrap/scripts/run.sh && diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index afce0eb70..58605f0de 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -1,12 +1,12 @@ [variables] -V1_ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +V1_ADDR = "" +V1_SEED = "" -V2_ADDR = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" -V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" +V2_ADDR = "" +V2_SEED = "" -V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" -V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" +V3_ADDR = "" +V3_SEED = "" PORT="8000" P2P_PORT="30335" @@ -24,14 +24,14 @@ host = "${domain}" path = "/" [config.env] -V1_ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +V1_ADDR = "" +V1_SEED = "" -V2_ADDR = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" -V2_SEED = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" +V2_ADDR = "" +V2_SEED = "" -V3_ADDR = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" -V3_SEED = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" +V3_ADDR = "" +V3_SEED = "" PORT="8000" -P2P_PORT="30335" \ No newline at end of file +P2P_PORT="30335" diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index d2ba72974..2ea1855b8 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -1,6 +1,9 @@ +version: "3.8" + services: reef-dev-cluster: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace environment: @@ -15,7 +18,7 @@ services: volumes: - ./:/workspace - ports: + expose: - "30335" - "30333" - "30334" @@ -35,7 +38,7 @@ services: echo '📥 Downloading cluster launcher...' && mkdir -p /workspace/dev-cluster && wget -q -O /workspace/dev-cluster/start-cluster.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/dev-cluster/start-cluster.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/dev-cluster/start-cluster.sh && echo '🔧 Making script executable...' && chmod +x /workspace/dev-cluster/start-cluster.sh && diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index a8f341dcd..c75b027e4 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -1,11 +1,11 @@ [variables] -v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" -v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" -v3sec = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" +v1sec = "" +v2sec = "" +v3sec = "" -v1addr = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -v2addr = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" -v3addr = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" +v1addr = "" +v2addr = "" +v3addr = "" specgenurl = "http://reef.host:8000" @@ -52,12 +52,12 @@ host = "${domain}" path = "/" [config.env] -v1sec ="0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" -v2sec = "0x3348e15287dae612d9c8f008a468ff6c229a55c4b58d778f389cc03134e3efbe" -v3sec = "0xba8ad5d3607f10c356860024a08ea279dbd529f0950fa752fc6c2d59981b89e5" +v1sec = "" +v2sec = "" +v3sec = "" -v1addr = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -v2addr = "5CmKswtRQGFrt3CcP5waJVxgEJfRy1xsmwPLRPWkumLBz8nv" -v3addr = "5HTmW3wtfs6h1D44QxfLPpwVzQ3Jc7S8eiQAuDRkmqEhrwMQ" +v1addr = "" +v2addr = "" +v3addr = "" specgenurl = "http://reef.host:8000" diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index 888617044..b7452b426 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -1,13 +1,16 @@ +version: "3.8" + services: reef-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:linux + restart: unless-stopped working_dir: /workspace volumes: - ./output:/output environment: - PORT=${PORT} - NODE_RPC_URL=${NODE_RPC_URL} - ports: - - "${PORT}:${PORT}" + expose: + - "${PORT}" entrypoint: /bin/sh command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-external --rpc-cors all" diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml index 6a3c06f08..4e6ebb15e 100644 --- a/blueprints/reef-faucet/docker-compose.yml +++ b/blueprints/reef-faucet/docker-compose.yml @@ -1,10 +1,13 @@ +version: "3.8" + services: reef-faucet: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace - ports: - - "${PORT}:${PORT}" + expose: + - "${PORT}" environment: WS_ENDPOINT: ${WS_ENDPOINT} @@ -28,9 +31,9 @@ services: echo '📥 Downloading faucet sources...' && mkdir -p /workspace/faucet && wget -O /workspace/faucet/package.json \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/faucet/package.json && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/faucet/package.json && wget -O /workspace/faucet/server.js \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/faucet/server.js && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/faucet/server.js && echo '📦 Installing faucet npm dependencies...' && cd /workspace/faucet && diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml index 7b3aee925..36b855ed5 100644 --- a/blueprints/reef-faucet/template.toml +++ b/blueprints/reef-faucet/template.toml @@ -2,7 +2,7 @@ PORT = "8080" WS_ENDPOINT = "ws://reef.host:9944" EVM_RPC_URL = "http://reef.host:8545" -FAUCET_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +FAUCET_SEED = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" @@ -16,6 +16,6 @@ path = "/" PORT = "8080" WS_ENDPOINT = "ws://reef.host:9944" EVM_RPC_URL = "http://reef.host:8545" -FAUCET_SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +FAUCET_SEED = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index d23729482..434f45e8e 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -1,11 +1,14 @@ +version: "3.8" + services: reef-keygen: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace volumes: - ./output:/output - ports: - - "${PORT}:${PORT}" + expose: + - "${PORT}" entrypoint: /bin/sh command: -c " set -e && diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index 214b57160..9ddaf80de 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -1,13 +1,14 @@ -version: '3.8' +version: "3.8" services: reef-rpc: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace - ports: - - "${P2P_PORT}:${P2P_PORT}" - - "${RPC_PORT}:${RPC_PORT}" + expose: + - "${P2P_PORT}" + - "${RPC_PORT}" environment: SPEC_URL: ${SPEC_URL} @@ -30,7 +31,7 @@ services: echo '📥 Downloading start-rpc.sh...' && mkdir -p /workspace/rpc/scripts && wget -O /workspace/rpc/scripts/start-rpc.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/rpc/scripts/start-rpc.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/rpc/scripts/start-rpc.sh && echo '🔧 Making script executable...' && chmod +x /workspace/rpc/scripts/start-rpc.sh && diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index 5c0c571b3..36e41a93b 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -1,14 +1,15 @@ -version: "3.9" +version: "3.8" services: - reef-spec-api: + reef-spec-gen: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace volumes: - ./output:/output - ports: + expose: - "8000" entrypoint: /bin/sh @@ -21,17 +22,17 @@ services: mkdir -p /workspace/scripts && wget -O /workspace/scripts/update-spec.py \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/scripts/update-spec.py && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/spec-generator/scripts/update-spec.py && wget -O /workspace/scripts/update-spec.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/scripts/update-spec.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/spec-generator/scripts/update-spec.sh && wget -O /workspace/api.py \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/spec-generator/api/api.py && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/spec-generator/api/api.py && chmod +x /workspace/scripts/update-spec.sh && - pip3 install flask && + pip3 install flask==3.1.0 && echo '🚀 Starting Spec Generator API on port 8000...' && python3 /workspace/api.py" diff --git a/blueprints/reef-spec-gen/template.toml b/blueprints/reef-spec-gen/template.toml index 4df78b66e..59d7532e6 100644 --- a/blueprints/reef-spec-gen/template.toml +++ b/blueprints/reef-spec-gen/template.toml @@ -1,7 +1,7 @@ [variables] [[config.domains]] -serviceName = "reef-spec-api" +serviceName = "reef-spec-gen" port = 8000 host = "${domain}" path = "/" diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index 6ea4805ca..7dcc5b444 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -1,12 +1,13 @@ -version: '3.8' +version: "3.8" services: reef-validator: image: anukulpandey/reef-chain-node:linux-2 + restart: unless-stopped working_dir: /workspace - ports: - - "${P2P_PORT}:${P2P_PORT}" + expose: + - "${P2P_PORT}" environment: SPEC_URL: ${SPEC_URL} @@ -31,7 +32,7 @@ services: echo '📥 Downloading start-validator.sh...' && mkdir -p /workspace/validator/scripts && wget -O /workspace/validator/scripts/start-validator.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/main/validator/scripts/start-validator.sh && + https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/validator/scripts/start-validator.sh && echo '🔧 Making script executable...' && chmod +x /workspace/validator/scripts/start-validator.sh && diff --git a/blueprints/reef-validator/template.toml b/blueprints/reef-validator/template.toml index f64b18640..6b3f1a55b 100644 --- a/blueprints/reef-validator/template.toml +++ b/blueprints/reef-validator/template.toml @@ -1,6 +1,6 @@ [variables] V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +V1SEED = "" SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" @@ -19,7 +19,7 @@ path = "/" [config.env] V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" -V1SEED = "0x4beb11e380012110b0b072fbb3d8e7455921cf4658de06c33d970be82ccf9ed5" +V1SEED = "" SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" @@ -28,4 +28,4 @@ BOOTNODE_IP="72.60.35.83" VALIDATOR_NAME="Validator-1" -P2P_PORT="30333" \ No newline at end of file +P2P_PORT="30333" From e7f555ab6d848e4f1c4c637e26a9f1fa23c69c61 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 17:10:01 +0530 Subject: [PATCH 67/90] chore: process meta.json --- meta.json | 54 ++++++++++++++++++------------------------------------ 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/meta.json b/meta.json index 6b0d8b363..c2d8cb313 100644 --- a/meta.json +++ b/meta.json @@ -5481,6 +5481,24 @@ "keys" ] }, + { + "id": "reef-faucet", + "name": "Reef Chain - Faucet", + "version": "1", + "description": "HTTP faucet for sending native Reef funds to EVM addresses on a custom Reef Chain cluster", + "links": { + "github": "https://github.com/anukulpandey/dokploy-reef-chain-scripts", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "faucet", + "self-hosted", + "evm" + ] + }, { "id": "reef-keygen", "name": "Reef Chain - Keys Generator", @@ -5553,24 +5571,6 @@ "reef-validator" ] }, - { - "id": "reef-faucet", - "name": "Reef Chain - Faucet", - "version": "1", - "description": "HTTP faucet for sending native Reef funds to EVM addresses on a custom Reef Chain cluster", - "links": { - "github": "https://github.com/anukulpandey/dokploy-reef-chain-scripts", - "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", - "docs": "https://docs.reef.io/" - }, - "logo": "reef.svg", - "tags": [ - "reef-chain", - "faucet", - "self-hosted", - "evm" - ] - }, { "id": "registry", "name": "Docker Registry", @@ -6096,24 +6096,6 @@ "media" ] }, - { - "id": "strapi", - "name": "Strapi", - "version": "v5.33.0", - "description": "Open-source headless CMS to build powerful APIs with built-in content management.", - "logo": "strapi.svg", - "links": { - "github": "https://github.com/strapi/strapi", - "discord": "https://discord.com/invite/strapi", - "docs": "https://docs.strapi.io", - "website": "https://strapi.io" - }, - "tags": [ - "headless", - "cms", - "content-management" - ] - }, { "id": "supabase", "name": "SupaBase", From c2fa2ee3e7b7f9f76162b709da8bd51a5628767c Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 17:21:35 +0530 Subject: [PATCH 68/90] fix: route reef spec generator via subdomain --- blueprints/reef-dev-cluster/template.toml | 4 ++-- blueprints/reef-spec-gen/template.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index c75b027e4..ac6633ec5 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -7,7 +7,7 @@ v1addr = "" v2addr = "" v3addr = "" -specgenurl = "http://reef.host:8000" +specgenurl = "https://spec.${domain}" [[config.domains]] serviceName = "reef-dev-cluster" @@ -60,4 +60,4 @@ v1addr = "" v2addr = "" v3addr = "" -specgenurl = "http://reef.host:8000" +specgenurl = "https://spec.${domain}" diff --git a/blueprints/reef-spec-gen/template.toml b/blueprints/reef-spec-gen/template.toml index 59d7532e6..86034cbde 100644 --- a/blueprints/reef-spec-gen/template.toml +++ b/blueprints/reef-spec-gen/template.toml @@ -3,7 +3,7 @@ [[config.domains]] serviceName = "reef-spec-gen" port = 8000 -host = "${domain}" +host = "spec.${domain}" path = "/" [config.env] From b9c8876f8e517d5222ad1b4cd680fba13c33c441 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 17:26:09 +0530 Subject: [PATCH 69/90] feat: make reef dev cluster self-contained --- .../reef-dev-cluster/docker-compose.yml | 21 +- blueprints/reef-dev-cluster/run-cluster.sh | 227 ++++++++++++++++++ blueprints/reef-dev-cluster/template.toml | 28 ++- meta.json | 5 +- 4 files changed, 253 insertions(+), 28 deletions(-) create mode 100755 blueprints/reef-dev-cluster/run-cluster.sh diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 2ea1855b8..314bcf069 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -7,13 +7,12 @@ services: working_dir: /workspace environment: - v1addr: "${v1addr}" - v2addr: "${v2addr}" - v3addr: "${v3addr}" v1sec: "${v1sec}" v2sec: "${v2sec}" v3sec: "${v3sec}" - specgenurl: "${specgenurl}" + faucetsec: "${faucetsec}" + DEFAULT_AMOUNT: "${DEFAULT_AMOUNT}" + MAX_AMOUNT: "${MAX_AMOUNT}" volumes: - ./:/workspace @@ -26,6 +25,7 @@ services: - "8001" - "9944" - "8545" + - "8080" entrypoint: /bin/sh command: -c " @@ -33,15 +33,10 @@ services: echo '📦 Installing required tools...' && apt-get update && - apt-get install -y wget curl python3 ca-certificates && - - echo '📥 Downloading cluster launcher...' && - mkdir -p /workspace/dev-cluster && - wget -q -O /workspace/dev-cluster/start-cluster.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/dev-cluster/start-cluster.sh && + apt-get install -y wget curl python3 nodejs npm ca-certificates && echo '🔧 Making script executable...' && - chmod +x /workspace/dev-cluster/start-cluster.sh && + chmod +x /workspace/run-cluster.sh && - echo '🚀 Starting local network...' && - exec /workspace/dev-cluster/start-cluster.sh" + echo '🚀 Starting self-contained network...' && + exec /workspace/run-cluster.sh" diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh new file mode 100755 index 000000000..54b66b167 --- /dev/null +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -0,0 +1,227 @@ +#!/usr/bin/env bash + +set -euo pipefail + +NODE_BIN="${NODE_BIN:-reef-node}" +REVIVE_BIN="${REVIVE_BIN:-eth-rpc}" +CHAIN_TEMPLATE="${CHAIN_TEMPLATE:-testnet-new}" +WORK_DIR="${WORK_DIR:-/tmp/reef-dev-cluster}" +OUTPUT_DIR="${OUTPUT_DIR:-/workspace/download}" +PLAIN_SPEC="${PLAIN_SPEC:-$WORK_DIR/local-chain-spec.json}" +UPDATED_SPEC="${UPDATED_SPEC:-$WORK_DIR/local-chain-spec-updated.json}" +SPEC_FILE="${SPEC_FILE:-$OUTPUT_DIR/local-chain-spec-raw.json}" +SPEC_HTTP_PORT="${SPEC_HTTP_PORT:-8001}" +ETH_RPC_PORT="${ETH_RPC_PORT:-8545}" +RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" +RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" +FAUCET_PORT="${FAUCET_PORT:-8080}" +DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-1}" +MAX_AMOUNT="${MAX_AMOUNT:-10}" +FAUCET_SEED="${faucetsec:-${v1sec:-}}" +SCRIPTS_COMMIT="52c3e30d06bba10936aea2db9740eafd76b3ac40" + +for var in v1sec v2sec v3sec; do + if [ -z "${!var:-}" ]; then + echo "Missing required environment variable: $var" >&2 + exit 1 + fi +done + +if [ -z "$FAUCET_SEED" ]; then + echo "Missing required faucet seed. Set faucetsec or v1sec." >&2 + exit 1 +fi + +PIDS=() +TAIL_PID="" + +cleanup() { + kill "$TAIL_PID" 2>/dev/null || true + if [ "${#PIDS[@]}" -gt 0 ]; then + kill "${PIDS[@]}" 2>/dev/null || true + fi +} + +trap cleanup EXIT + +derive_address() { + local suri="$1" + + "$NODE_BIN" key inspect --scheme Sr25519 "$suri" --output-type json \ + | grep -o '"ss58Address": "[^"]*"' \ + | cut -d'"' -f4 +} + +insert_keys() { + local base_path="$1" + local seed="$2" + + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//babe" --key-type babe + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Ed25519 --suri "$seed//grandpa" --key-type gran + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//im_online" --key-type imon + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//authority_discovery" --key-type audi +} + +start_logged() { + local log_file="$1" + shift + + "$@" >"$log_file" 2>&1 & + PIDS+=("$!") +} + +V1_ADDR=$(derive_address "$v1sec") +V2_ADDR=$(derive_address "$v2sec") +V3_ADDR=$(derive_address "$v3sec") + +HELPER_DIR="$WORK_DIR/spec-generator" +FAUCET_DIR="$WORK_DIR/faucet" + +rm -rf "$WORK_DIR" "$OUTPUT_DIR" /tmp/validator1 /tmp/validator2 /tmp/validator3 /tmp/rpc-node /tmp/bootnode +mkdir -p "$HELPER_DIR" "$FAUCET_DIR" "$OUTPUT_DIR" + +echo "Downloading self-contained cluster helpers..." +wget -q -O "$HELPER_DIR/update-spec.py" \ + "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.py" +wget -q -O "$HELPER_DIR/update-spec.sh" \ + "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.sh" +wget -q -O "$FAUCET_DIR/package.json" \ + "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/faucet/package.json" +wget -q -O "$FAUCET_DIR/server.js" \ + "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/faucet/server.js" +chmod +x "$HELPER_DIR/update-spec.sh" + +echo "Installing faucet dependencies..." +( + cd "$FAUCET_DIR" + npm install --omit=dev +) + +echo "Generating chain spec..." +"$NODE_BIN" build-spec --chain "$CHAIN_TEMPLATE" --disable-default-bootnode > "$PLAIN_SPEC" + +"$HELPER_DIR/update-spec.sh" \ + --v1_addr "$V1_ADDR" --v1_sec "$v1sec" \ + --v2_addr "$V2_ADDR" --v2_sec "$v2sec" \ + --v3_addr "$V3_ADDR" --v3_sec "$v3sec" \ + --input "$PLAIN_SPEC" \ + --output "$UPDATED_SPEC" + +"$NODE_BIN" build-spec \ + --chain "$UPDATED_SPEC" \ + --disable-default-bootnode \ + --raw > "$SPEC_FILE" + +echo "Generating node identities..." +BOOTNODE_NODE_KEY_FILE="$WORK_DIR/bootnode_node_key.txt" +V1_NODE_KEY_FILE="$WORK_DIR/v1_node_key.txt" +V2_NODE_KEY_FILE="$WORK_DIR/v2_node_key.txt" +V3_NODE_KEY_FILE="$WORK_DIR/v3_node_key.txt" +RPC_NODE_KEY_FILE="$WORK_DIR/rpc_node_key.txt" + +"$NODE_BIN" key generate-node-key --chain local > "$BOOTNODE_NODE_KEY_FILE" +"$NODE_BIN" key generate-node-key --chain local > "$V1_NODE_KEY_FILE" +"$NODE_BIN" key generate-node-key --chain local > "$V2_NODE_KEY_FILE" +"$NODE_BIN" key generate-node-key --chain local > "$V3_NODE_KEY_FILE" +"$NODE_BIN" key generate-node-key --chain local > "$RPC_NODE_KEY_FILE" + +BOOTNODE_PEER_ID=$("$NODE_BIN" key inspect-node-key --file "$BOOTNODE_NODE_KEY_FILE" 2>/dev/null | tail -n1 | tr -d '\r') +if [ -z "$BOOTNODE_PEER_ID" ]; then + echo "Failed to derive bootnode peer id" >&2 + exit 1 +fi + +printf '%s\n' "$BOOTNODE_PEER_ID" > "$OUTPUT_DIR/bootnode_node_key.txt" +printf '%s\n' "$BOOTNODE_PEER_ID" > "$OUTPUT_DIR/bootnode_peer_id.txt" + +insert_keys /tmp/validator1 "$v1sec" +insert_keys /tmp/validator2 "$v2sec" +insert_keys /tmp/validator3 "$v3sec" + +python3 -m http.server "$SPEC_HTTP_PORT" --bind 0.0.0.0 --directory "$OUTPUT_DIR" >/tmp/spec-server.log 2>&1 & +PIDS+=("$!") + +BOOTNODE_MULTIADDR="/ip4/127.0.0.1/tcp/30335/p2p/$BOOTNODE_PEER_ID" + +start_logged /tmp/bootnode.log \ + "$NODE_BIN" \ + --base-path /tmp/bootnode \ + --chain "$SPEC_FILE" \ + --port 30335 \ + --node-key-file "$BOOTNODE_NODE_KEY_FILE" \ + --name Bootnode \ + --no-telemetry + +start_logged /tmp/validator1.log \ + "$NODE_BIN" \ + --base-path /tmp/validator1 \ + --chain "$SPEC_FILE" \ + --port 30333 \ + --node-key-file "$V1_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator1 \ + --no-telemetry + +start_logged /tmp/validator2.log \ + "$NODE_BIN" \ + --base-path /tmp/validator2 \ + --chain "$SPEC_FILE" \ + --port 30334 \ + --node-key-file "$V2_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator2 \ + --no-telemetry + +start_logged /tmp/validator3.log \ + "$NODE_BIN" \ + --base-path /tmp/validator3 \ + --chain "$SPEC_FILE" \ + --port 30336 \ + --node-key-file "$V3_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator3 \ + --no-telemetry + +start_logged /tmp/rpc-node.log \ + "$NODE_BIN" \ + --base-path /tmp/rpc-node \ + --chain "$SPEC_FILE" \ + --port "$RPC_NODE_P2P_PORT" \ + --node-key-file "$RPC_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --rpc-external \ + --rpc-port "$RPC_NODE_WS_PORT" \ + --rpc-cors all \ + --rpc-methods Unsafe \ + --rpc-max-connections 10000 \ + --pruning archive \ + --name rpc-node \ + --no-telemetry + +start_logged /tmp/eth-rpc.log \ + "$REVIVE_BIN" \ + --node-rpc-url "ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ + --rpc-port "$ETH_RPC_PORT" \ + --rpc-cors all \ + --rpc-external \ + --rpc-methods Unsafe + +start_logged /tmp/faucet.log \ + env \ + PORT="$FAUCET_PORT" \ + WS_ENDPOINT="ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ + EVM_RPC_URL="http://127.0.0.1:${ETH_RPC_PORT}" \ + FAUCET_SEED="$FAUCET_SEED" \ + DEFAULT_AMOUNT="$DEFAULT_AMOUNT" \ + MAX_AMOUNT="$MAX_AMOUNT" \ + npm --prefix "$FAUCET_DIR" start + +tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/eth-rpc.log /tmp/faucet.log & +TAIL_PID=$! + +wait -n "${PIDS[@]}" +STATUS=$? +exit "$STATUS" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index ac6633ec5..8a72c7e74 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -3,11 +3,9 @@ v1sec = "" v2sec = "" v3sec = "" -v1addr = "" -v2addr = "" -v3addr = "" - -specgenurl = "https://spec.${domain}" +faucetsec = "" +DEFAULT_AMOUNT = "1" +MAX_AMOUNT = "10" [[config.domains]] serviceName = "reef-dev-cluster" @@ -36,19 +34,25 @@ path = "/" [[config.domains]] serviceName = "reef-dev-cluster" port = 8001 -host = "${domain}" +host = "spec.${domain}" path = "/" [[config.domains]] serviceName = "reef-dev-cluster" port = 9944 -host = "${domain}" +host = "ws.${domain}" path = "/" [[config.domains]] serviceName = "reef-dev-cluster" port = 8545 -host = "${domain}" +host = "eth.${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8080 +host = "faucet.${domain}" path = "/" [config.env] @@ -56,8 +60,6 @@ v1sec = "" v2sec = "" v3sec = "" -v1addr = "" -v2addr = "" -v3addr = "" - -specgenurl = "https://spec.${domain}" +faucetsec = "" +DEFAULT_AMOUNT = "1" +MAX_AMOUNT = "10" diff --git a/meta.json b/meta.json index c2d8cb313..51914512e 100644 --- a/meta.json +++ b/meta.json @@ -5449,7 +5449,7 @@ "id": "reef-dev-cluster", "name": "Reef Chain - Dev Cluster", "version": "1", - "description": "Development cluster", + "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, EVM RPC, and faucet", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", @@ -5460,7 +5460,8 @@ "reef-chain", "dev-cluster", "self-hosted", - "evm" + "evm", + "faucet" ] }, { From 06e46ec2b8ba1663e702052bb6c30c184b57999b Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:06:10 +0530 Subject: [PATCH 70/90] fix: make reef templates amd64 compatible --- blueprints/reef-bootnode/docker-compose.yml | 2 +- .../reef-dev-cluster/docker-compose.yml | 10 +- .../reef-dev-cluster/faucet/package.json | 13 + blueprints/reef-dev-cluster/faucet/server.js | 273 ++++++++++++++++++ blueprints/reef-dev-cluster/run-cluster.sh | 21 +- blueprints/reef-dev-cluster/template.toml | 6 - blueprints/reef-faucet/docker-compose.yml | 9 +- blueprints/reef-keygen/docker-compose.yml | 2 +- blueprints/reef-rpc/docker-compose.yml | 2 +- blueprints/reef-spec-gen/docker-compose.yml | 2 +- blueprints/reef-validator/docker-compose.yml | 2 +- meta.json | 2 +- 12 files changed, 309 insertions(+), 35 deletions(-) create mode 100644 blueprints/reef-dev-cluster/faucet/package.json create mode 100644 blueprints/reef-dev-cluster/faucet/server.js diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 80efc779c..45b00d013 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-bootnode: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 314bcf069..4615a0792 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-dev-cluster: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace @@ -24,7 +24,6 @@ services: - "30336" - "8001" - "9944" - - "8545" - "8080" entrypoint: /bin/sh @@ -33,7 +32,12 @@ services: echo '📦 Installing required tools...' && apt-get update && - apt-get install -y wget curl python3 nodejs npm ca-certificates && + apt-get install -y wget curl python3 ca-certificates gnupg && + mkdir -p /etc/apt/keyrings && + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && + echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list && + apt-get update && + apt-get install -y nodejs && echo '🔧 Making script executable...' && chmod +x /workspace/run-cluster.sh && diff --git a/blueprints/reef-dev-cluster/faucet/package.json b/blueprints/reef-dev-cluster/faucet/package.json new file mode 100644 index 000000000..fd45ff3d7 --- /dev/null +++ b/blueprints/reef-dev-cluster/faucet/package.json @@ -0,0 +1,13 @@ +{ + "name": "reef-dev-cluster-faucet", + "version": "1.0.0", + "private": true, + "description": "HTTP faucet for a self-contained Reef dev cluster", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "@polkadot/api": "^16.4.8" + } +} diff --git a/blueprints/reef-dev-cluster/faucet/server.js b/blueprints/reef-dev-cluster/faucet/server.js new file mode 100644 index 000000000..ad53d53b9 --- /dev/null +++ b/blueprints/reef-dev-cluster/faucet/server.js @@ -0,0 +1,273 @@ +#!/usr/bin/env node + +const http = require("node:http"); +const { ApiPromise, Keyring, WsProvider } = require("@polkadot/api"); + +const config = { + wsEndpoint: process.env.WS_ENDPOINT || "ws://127.0.0.1:9944", + faucetSeed: process.env.FAUCET_SEED || "", + port: Number.parseInt(process.env.PORT || "8080", 10), + defaultAmount: String(process.env.DEFAULT_AMOUNT || "1"), + maxAmount: String(process.env.MAX_AMOUNT || "10"), +}; + +if (!config.faucetSeed) { + throw new Error("FAUCET_SEED is required"); +} + +let contextPromise = null; +let requestQueue = Promise.resolve(); + +function normalizeEvmAddress(value) { + const normalized = String(value || "").trim(); + if (!/^0x[0-9a-fA-F]{40}$/.test(normalized)) { + throw new Error(`Invalid EVM address: ${value}`); + } + return normalized; +} + +function parseUnits(value, decimals) { + const input = String(value).trim(); + if (!/^\d+(\.\d+)?$/.test(input)) { + throw new Error(`Invalid decimal amount: ${value}`); + } + + const [whole, fraction = ""] = input.split("."); + if (fraction.length > decimals) { + throw new Error(`Too many decimal places in ${value}`); + } + + const wholeUnits = BigInt(whole) * 10n ** BigInt(decimals); + const fractionUnits = fraction ? BigInt(fraction.padEnd(decimals, "0")) : 0n; + return wholeUnits + fractionUnits; +} + +function formatUnits(value, decimals) { + const negative = value < 0n; + const abs = negative ? -value : value; + const base = 10n ** BigInt(decimals); + const whole = abs / base; + const fraction = abs % base; + + if (fraction === 0n) { + return `${negative ? "-" : ""}${whole.toString()}`; + } + + return `${negative ? "-" : ""}${whole.toString()}.${fraction + .toString() + .padStart(decimals, "0") + .replace(/0+$/, "")}`; +} + +function fallbackAccountFromEvm(evmAddress) { + return `0x${evmAddress.slice(2).toLowerCase()}${"ee".repeat(12)}`; +} + +function decodeDispatchError(api, dispatchError) { + if (dispatchError && dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + return `${decoded.section}.${decoded.name}: ${decoded.docs.join(" ")}`; + } + + return dispatchError && dispatchError.toString ? dispatchError.toString() : String(dispatchError); +} + +async function signAndWait(api, tx, signer) { + return new Promise(async (resolve, reject) => { + let unsubscribe = null; + + try { + unsubscribe = await tx.signAndSend(signer, ({ dispatchError, events, status, txHash }) => { + if (dispatchError) { + if (unsubscribe) { + unsubscribe(); + } + reject(new Error(decodeDispatchError(api, dispatchError))); + return; + } + + if (status.isFinalized) { + if (unsubscribe) { + unsubscribe(); + } + resolve({ + txHash: txHash.toHex(), + finalizedBlock: status.asFinalized.toHex(), + eventCount: events.length, + }); + } + }); + } catch (error) { + if (unsubscribe) { + unsubscribe(); + } + reject(error); + } + }); +} + +async function maybeClaimDefaultAccount(api, signer) { + const storage = api.query && api.query.evmAccounts && api.query.evmAccounts.evmAddresses; + const extrinsic = api.tx && api.tx.evmAccounts && api.tx.evmAccounts.claimDefaultAccount; + + if (!storage || !extrinsic) { + return null; + } + + const current = await storage(signer.address); + if (!current.isEmpty) { + return current.toString(); + } + + await signAndWait(api, extrinsic(), signer); + return (await storage(signer.address)).toString(); +} + +function buildReviveTransfer(api, signerAddress, targetEvm, amountUnits) { + const reviveTransfer = api.tx && api.tx.revive && api.tx.revive.transfer; + if (!reviveTransfer) { + return null; + } + + const metaArgs = reviveTransfer.meta.toJSON().args || []; + const names = metaArgs.map((arg) => String((arg && arg.name) || "").toLowerCase()); + const types = metaArgs.map((arg) => JSON.stringify((arg && arg.type) || "").toLowerCase()); + const haystack = `${names.join(",")}|${types.join(",")}`; + + if (metaArgs.length === 2 && haystack.includes("h160")) { + return { + strategy: "revive.transfer(H160, Balance)", + tx: reviveTransfer(targetEvm, amountUnits), + }; + } + + if (metaArgs.length === 3 && haystack.includes("accountid") && haystack.includes("h160")) { + return { + strategy: "revive.transfer(AccountId, H160, Balance)", + tx: reviveTransfer(signerAddress, targetEvm, amountUnits), + }; + } + + return { + strategy: "revive.transfer(fallback call signature)", + tx: reviveTransfer(targetEvm, amountUnits), + }; +} + +async function getContext() { + if (!contextPromise) { + contextPromise = (async () => { + const provider = new WsProvider(config.wsEndpoint); + const api = await ApiPromise.create({ provider }); + const keyring = new Keyring({ type: "sr25519" }); + const sender = keyring.addFromUri(config.faucetSeed); + const nativeDecimals = api.registry.chainDecimals[0] || 12; + const tokenSymbol = api.registry.chainTokens[0] || "REEF"; + + return { api, nativeDecimals, provider, sender, tokenSymbol }; + })(); + } + + return contextPromise; +} + +function jsonResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "application/json" }); + response.end(JSON.stringify(body)); +} + +async function parseRequestBody(request) { + const chunks = []; + for await (const chunk of request) { + chunks.push(chunk); + } + + if (chunks.length === 0) { + return {}; + } + + return JSON.parse(Buffer.concat(chunks).toString("utf8")); +} + +async function executeDrip(targetEvm, amountText) { + const { api, nativeDecimals, sender, tokenSymbol } = await getContext(); + const amountUnits = parseUnits(amountText, nativeDecimals); + const maxUnits = parseUnits(config.maxAmount, nativeDecimals); + + if (amountUnits <= 0n) { + throw new Error("Amount must be greater than zero"); + } + + if (amountUnits > maxUnits) { + throw new Error(`Amount exceeds MAX_AMOUNT (${config.maxAmount})`); + } + + await maybeClaimDefaultAccount(api, sender); + + const senderBefore = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + let strategy = ""; + let result = null; + + try { + const revive = buildReviveTransfer(api, sender.address, targetEvm, amountUnits); + if (!revive) { + throw new Error("revive.transfer unavailable"); + } + strategy = revive.strategy; + result = await signAndWait(api, revive.tx, sender); + } catch (error) { + strategy = "balances.transferAllowDeath(AccountId32 fallback)"; + result = await signAndWait( + api, + api.tx.balances.transferAllowDeath(fallbackAccountFromEvm(targetEvm), amountUnits), + sender + ); + } + + const senderAfter = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + return { + amount: amountText, + strategy, + tokenSymbol, + txHash: result.txHash, + finalizedBlock: result.finalizedBlock, + eventCount: result.eventCount, + sender: sender.address, + senderNativeBefore: formatUnits(senderBefore, nativeDecimals), + senderNativeAfter: formatUnits(senderAfter, nativeDecimals), + }; +} + +function enqueue(task) { + const current = requestQueue.then(task, task); + requestQueue = current.catch(() => undefined); + return current; +} + +const server = http.createServer(async (request, response) => { + try { + if (request.method === "GET" && request.url === "/health") { + jsonResponse(response, 200, { status: "ok" }); + return; + } + + if (request.method === "POST" && request.url === "/drip") { + const payload = await parseRequestBody(request); + const targetEvm = normalizeEvmAddress(payload.to); + const amount = payload.amount ? String(payload.amount) : config.defaultAmount; + const result = await enqueue(() => executeDrip(targetEvm, amount)); + jsonResponse(response, 200, result); + return; + } + + jsonResponse(response, 404, { error: "Not found" }); + } catch (error) { + jsonResponse(response, 400, { error: error.message }); + } +}); + +server.listen(config.port, "0.0.0.0", () => { + console.log(`reef dev-cluster faucet listening on ${config.port}`); +}); diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 54b66b167..4ffccd6f0 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -3,7 +3,6 @@ set -euo pipefail NODE_BIN="${NODE_BIN:-reef-node}" -REVIVE_BIN="${REVIVE_BIN:-eth-rpc}" CHAIN_TEMPLATE="${CHAIN_TEMPLATE:-testnet-new}" WORK_DIR="${WORK_DIR:-/tmp/reef-dev-cluster}" OUTPUT_DIR="${OUTPUT_DIR:-/workspace/download}" @@ -11,7 +10,6 @@ PLAIN_SPEC="${PLAIN_SPEC:-$WORK_DIR/local-chain-spec.json}" UPDATED_SPEC="${UPDATED_SPEC:-$WORK_DIR/local-chain-spec-updated.json}" SPEC_FILE="${SPEC_FILE:-$OUTPUT_DIR/local-chain-spec-raw.json}" SPEC_HTTP_PORT="${SPEC_HTTP_PORT:-8001}" -ETH_RPC_PORT="${ETH_RPC_PORT:-8545}" RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" FAUCET_PORT="${FAUCET_PORT:-8080}" @@ -75,20 +73,16 @@ V2_ADDR=$(derive_address "$v2sec") V3_ADDR=$(derive_address "$v3sec") HELPER_DIR="$WORK_DIR/spec-generator" -FAUCET_DIR="$WORK_DIR/faucet" +FAUCET_DIR="/workspace/faucet" rm -rf "$WORK_DIR" "$OUTPUT_DIR" /tmp/validator1 /tmp/validator2 /tmp/validator3 /tmp/rpc-node /tmp/bootnode -mkdir -p "$HELPER_DIR" "$FAUCET_DIR" "$OUTPUT_DIR" +mkdir -p "$HELPER_DIR" "$OUTPUT_DIR" echo "Downloading self-contained cluster helpers..." wget -q -O "$HELPER_DIR/update-spec.py" \ "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.py" wget -q -O "$HELPER_DIR/update-spec.sh" \ "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.sh" -wget -q -O "$FAUCET_DIR/package.json" \ - "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/faucet/package.json" -wget -q -O "$FAUCET_DIR/server.js" \ - "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/faucet/server.js" chmod +x "$HELPER_DIR/update-spec.sh" echo "Installing faucet dependencies..." @@ -201,25 +195,16 @@ start_logged /tmp/rpc-node.log \ --name rpc-node \ --no-telemetry -start_logged /tmp/eth-rpc.log \ - "$REVIVE_BIN" \ - --node-rpc-url "ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ - --rpc-port "$ETH_RPC_PORT" \ - --rpc-cors all \ - --rpc-external \ - --rpc-methods Unsafe - start_logged /tmp/faucet.log \ env \ PORT="$FAUCET_PORT" \ WS_ENDPOINT="ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ - EVM_RPC_URL="http://127.0.0.1:${ETH_RPC_PORT}" \ FAUCET_SEED="$FAUCET_SEED" \ DEFAULT_AMOUNT="$DEFAULT_AMOUNT" \ MAX_AMOUNT="$MAX_AMOUNT" \ npm --prefix "$FAUCET_DIR" start -tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/eth-rpc.log /tmp/faucet.log & +tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/faucet.log & TAIL_PID=$! wait -n "${PIDS[@]}" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index 8a72c7e74..fc2f55bcc 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -43,12 +43,6 @@ port = 9944 host = "ws.${domain}" path = "/" -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 8545 -host = "eth.${domain}" -path = "/" - [[config.domains]] serviceName = "reef-dev-cluster" port = 8080 diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml index 4e6ebb15e..669236ca9 100644 --- a/blueprints/reef-faucet/docker-compose.yml +++ b/blueprints/reef-faucet/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-faucet: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace @@ -26,7 +26,12 @@ services: echo '📦 Installing dependencies...' && apt-get update && - apt-get install -y nodejs npm wget ca-certificates && + apt-get install -y wget curl ca-certificates gnupg && + mkdir -p /etc/apt/keyrings && + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && + echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list && + apt-get update && + apt-get install -y nodejs && echo '📥 Downloading faucet sources...' && mkdir -p /workspace/faucet && diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index 434f45e8e..ceda49c07 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-keygen: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace volumes: diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index 9ddaf80de..3fc047cc0 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-rpc: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index 36e41a93b..c8462ad72 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-spec-gen: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index 7dcc5b444..aefcf88b2 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-validator: - image: anukulpandey/reef-chain-node:linux-2 + image: anukulpandey/reef-chain-node:linux restart: unless-stopped working_dir: /workspace diff --git a/meta.json b/meta.json index 51914512e..9030e96fa 100644 --- a/meta.json +++ b/meta.json @@ -5449,7 +5449,7 @@ "id": "reef-dev-cluster", "name": "Reef Chain - Dev Cluster", "version": "1", - "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, EVM RPC, and faucet", + "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, and faucet", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", From 20795c4bdb3cb17ca214373ddb4a4d170ee6e0b3 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:08:31 +0530 Subject: [PATCH 71/90] chore: update reef images to latest tags --- blueprints/reef-bootnode/docker-compose.yml | 2 +- blueprints/reef-dev-cluster/docker-compose.yml | 2 +- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- blueprints/reef-faucet/docker-compose.yml | 2 +- blueprints/reef-keygen/docker-compose.yml | 2 +- blueprints/reef-rpc/docker-compose.yml | 2 +- blueprints/reef-spec-gen/docker-compose.yml | 2 +- blueprints/reef-validator/docker-compose.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/blueprints/reef-bootnode/docker-compose.yml b/blueprints/reef-bootnode/docker-compose.yml index 45b00d013..85c9b2718 100644 --- a/blueprints/reef-bootnode/docker-compose.yml +++ b/blueprints/reef-bootnode/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-bootnode: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 4615a0792..8a2561240 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-dev-cluster: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index b7452b426..d13caf3b1 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-eth-rpc: - image: anukulpandey/reef-chain-eth-rpc:linux + image: anukulpandey/reef-chain-eth-rpc:latest restart: unless-stopped working_dir: /workspace volumes: diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml index 669236ca9..ae156e779 100644 --- a/blueprints/reef-faucet/docker-compose.yml +++ b/blueprints/reef-faucet/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-faucet: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml index ceda49c07..b6d332ce2 100644 --- a/blueprints/reef-keygen/docker-compose.yml +++ b/blueprints/reef-keygen/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-keygen: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace volumes: diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index 3fc047cc0..4f7fdb1d2 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-rpc: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-spec-gen/docker-compose.yml b/blueprints/reef-spec-gen/docker-compose.yml index c8462ad72..8f7baa274 100644 --- a/blueprints/reef-spec-gen/docker-compose.yml +++ b/blueprints/reef-spec-gen/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-spec-gen: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace diff --git a/blueprints/reef-validator/docker-compose.yml b/blueprints/reef-validator/docker-compose.yml index aefcf88b2..1caaa4784 100644 --- a/blueprints/reef-validator/docker-compose.yml +++ b/blueprints/reef-validator/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: reef-validator: - image: anukulpandey/reef-chain-node:linux + image: anukulpandey/reef-chain-node:latest restart: unless-stopped working_dir: /workspace From 02da0d9fd6f8d88cb949d38eb8af8adca56b10bf Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:13:15 +0530 Subject: [PATCH 72/90] fix: make reef dev cluster bootstrap portable --- blueprints/reef-dev-cluster/run-cluster.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 4ffccd6f0..788c55cc5 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -17,6 +17,8 @@ DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-1}" MAX_AMOUNT="${MAX_AMOUNT:-10}" FAUCET_SEED="${faucetsec:-${v1sec:-}}" SCRIPTS_COMMIT="52c3e30d06bba10936aea2db9740eafd76b3ac40" +TEMPLATE_ASSET_REF="${TEMPLATE_ASSET_REF:-reef-chain}" +TEMPLATE_ASSET_BASE_URL="${TEMPLATE_ASSET_BASE_URL:-https://raw.githubusercontent.com/anukulpandey/dokploy-templates/${TEMPLATE_ASSET_REF}/blueprints/reef-dev-cluster}" for var in v1sec v2sec v3sec; do if [ -z "${!var:-}" ]; then @@ -76,7 +78,7 @@ HELPER_DIR="$WORK_DIR/spec-generator" FAUCET_DIR="/workspace/faucet" rm -rf "$WORK_DIR" "$OUTPUT_DIR" /tmp/validator1 /tmp/validator2 /tmp/validator3 /tmp/rpc-node /tmp/bootnode -mkdir -p "$HELPER_DIR" "$OUTPUT_DIR" +mkdir -p "$HELPER_DIR" "$OUTPUT_DIR" "$FAUCET_DIR" echo "Downloading self-contained cluster helpers..." wget -q -O "$HELPER_DIR/update-spec.py" \ @@ -85,6 +87,12 @@ wget -q -O "$HELPER_DIR/update-spec.sh" \ "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.sh" chmod +x "$HELPER_DIR/update-spec.sh" +echo "Downloading local faucet sources..." +wget -q -O "$FAUCET_DIR/package.json" \ + "${TEMPLATE_ASSET_BASE_URL}/faucet/package.json" +wget -q -O "$FAUCET_DIR/server.js" \ + "${TEMPLATE_ASSET_BASE_URL}/faucet/server.js" + echo "Installing faucet dependencies..." ( cd "$FAUCET_DIR" From 2eed1f94cefca9cf7de187d69824d5ffbb46a3fb Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:13:54 +0530 Subject: [PATCH 73/90] fix: bootstrap reef dev cluster from pinned assets --- .../reef-dev-cluster/docker-compose.yml | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 8a2561240..8129c070d 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -14,9 +14,6 @@ services: DEFAULT_AMOUNT: "${DEFAULT_AMOUNT}" MAX_AMOUNT: "${MAX_AMOUNT}" - volumes: - - ./:/workspace - expose: - "30335" - "30333" @@ -27,20 +24,26 @@ services: - "8080" entrypoint: /bin/sh - command: -c " - set -e && - - echo '📦 Installing required tools...' && - apt-get update && - apt-get install -y wget curl python3 ca-certificates gnupg && - mkdir -p /etc/apt/keyrings && - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && - echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list && - apt-get update && - apt-get install -y nodejs && - - echo '🔧 Making script executable...' && - chmod +x /workspace/run-cluster.sh && - - echo '🚀 Starting self-contained network...' && - exec /workspace/run-cluster.sh" + command: + - -c + - | + set -e + + echo '📦 Installing required tools...' + apt-get update + apt-get install -y wget curl python3 ca-certificates gnupg + + mkdir -p /etc/apt/keyrings /workspace + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list + apt-get update + apt-get install -y nodejs + + echo '📥 Downloading cluster bootstrap...' + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/02da0d9fd6f8d88cb949d38eb8af8adca56b10bf/blueprints/reef-dev-cluster/run-cluster.sh + + echo '🔧 Making script executable...' + chmod +x /workspace/run-cluster.sh + + echo '🚀 Starting self-contained network...' + exec env TEMPLATE_ASSET_REF=02da0d9fd6f8d88cb949d38eb8af8adca56b10bf /workspace/run-cluster.sh From def2dca54efbc522d383d3aa67528a121233b9bc Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:55:33 +0530 Subject: [PATCH 74/90] fix: make reef dev cluster zero-config --- .../reef-dev-cluster/docker-compose.yml | 22 +- blueprints/reef-dev-cluster/run-cluster.sh | 394 +++++++++++++++--- blueprints/reef-dev-cluster/template.toml | 4 + 3 files changed, 356 insertions(+), 64 deletions(-) mode change 100755 => 100644 blueprints/reef-dev-cluster/run-cluster.sh diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 8129c070d..20b339cc3 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -7,12 +7,16 @@ services: working_dir: /workspace environment: - v1sec: "${v1sec}" - v2sec: "${v2sec}" - v3sec: "${v3sec}" - faucetsec: "${faucetsec}" - DEFAULT_AMOUNT: "${DEFAULT_AMOUNT}" - MAX_AMOUNT: "${MAX_AMOUNT}" + v1sec: "${v1sec:-}" + v2sec: "${v2sec:-}" + v3sec: "${v3sec:-}" + faucetsec: "${faucetsec:-}" + DEFAULT_AMOUNT: "${DEFAULT_AMOUNT:-1}" + MAX_AMOUNT: "${MAX_AMOUNT:-10}" + + volumes: + - reef-dev-cluster-state:/workspace/state + - reef-dev-cluster-download:/workspace/download expose: - "30335" @@ -33,7 +37,7 @@ services: apt-get update apt-get install -y wget curl python3 ca-certificates gnupg - mkdir -p /etc/apt/keyrings /workspace + mkdir -p /etc/apt/keyrings /workspace /workspace/state /workspace/download curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list apt-get update @@ -47,3 +51,7 @@ services: echo '🚀 Starting self-contained network...' exec env TEMPLATE_ASSET_REF=02da0d9fd6f8d88cb949d38eb8af8adca56b10bf /workspace/run-cluster.sh + +volumes: + reef-dev-cluster-state: + reef-dev-cluster-download: diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh old mode 100755 new mode 100644 index 788c55cc5..b7772f004 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -5,32 +5,34 @@ set -euo pipefail NODE_BIN="${NODE_BIN:-reef-node}" CHAIN_TEMPLATE="${CHAIN_TEMPLATE:-testnet-new}" WORK_DIR="${WORK_DIR:-/tmp/reef-dev-cluster}" +STATE_DIR="${STATE_DIR:-/workspace/state}" +SEED_DIR="${SEED_DIR:-$STATE_DIR/seeds}" +NODE_KEY_DIR="${NODE_KEY_DIR:-$STATE_DIR/node-keys}" OUTPUT_DIR="${OUTPUT_DIR:-/workspace/download}" PLAIN_SPEC="${PLAIN_SPEC:-$WORK_DIR/local-chain-spec.json}" UPDATED_SPEC="${UPDATED_SPEC:-$WORK_DIR/local-chain-spec-updated.json}" SPEC_FILE="${SPEC_FILE:-$OUTPUT_DIR/local-chain-spec-raw.json}" SPEC_HTTP_PORT="${SPEC_HTTP_PORT:-8001}" +BOOTNODE_P2P_PORT="${BOOTNODE_P2P_PORT:-30335}" +VALIDATOR1_P2P_PORT="${VALIDATOR1_P2P_PORT:-30333}" +VALIDATOR2_P2P_PORT="${VALIDATOR2_P2P_PORT:-30334}" +VALIDATOR3_P2P_PORT="${VALIDATOR3_P2P_PORT:-30336}" RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" FAUCET_PORT="${FAUCET_PORT:-8080}" +BOOTNODE_PROMETHEUS_PORT="${BOOTNODE_PROMETHEUS_PORT:-9615}" +VALIDATOR1_PROMETHEUS_PORT="${VALIDATOR1_PROMETHEUS_PORT:-9616}" +VALIDATOR2_PROMETHEUS_PORT="${VALIDATOR2_PROMETHEUS_PORT:-9617}" +VALIDATOR3_PROMETHEUS_PORT="${VALIDATOR3_PROMETHEUS_PORT:-9618}" +RPC_NODE_PROMETHEUS_PORT="${RPC_NODE_PROMETHEUS_PORT:-9619}" DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-1}" MAX_AMOUNT="${MAX_AMOUNT:-10}" -FAUCET_SEED="${faucetsec:-${v1sec:-}}" -SCRIPTS_COMMIT="52c3e30d06bba10936aea2db9740eafd76b3ac40" TEMPLATE_ASSET_REF="${TEMPLATE_ASSET_REF:-reef-chain}" TEMPLATE_ASSET_BASE_URL="${TEMPLATE_ASSET_BASE_URL:-https://raw.githubusercontent.com/anukulpandey/dokploy-templates/${TEMPLATE_ASSET_REF}/blueprints/reef-dev-cluster}" - -for var in v1sec v2sec v3sec; do - if [ -z "${!var:-}" ]; then - echo "Missing required environment variable: $var" >&2 - exit 1 - fi -done - -if [ -z "$FAUCET_SEED" ]; then - echo "Missing required faucet seed. Set faucetsec or v1sec." >&2 - exit 1 -fi +FAUCET_DIR="${FAUCET_DIR:-/workspace/faucet}" +BOOTNODE_PEER_ID_FILE="$OUTPUT_DIR/bootnode_peer_id.txt" +BOOTNODE_COMPAT_FILE="$OUTPUT_DIR/bootnode_node_key.txt" +CLUSTER_INFO_FILE="$OUTPUT_DIR/cluster-info.json" PIDS=() TAIL_PID="" @@ -44,10 +46,122 @@ cleanup() { trap cleanup EXIT +trim_whitespace() { + printf '%s' "$1" | tr -d '[:space:]' +} + +canonicalize_seed() { + local name="$1" + local value + value=$(trim_whitespace "${2:-}") + + if [ -z "$value" ]; then + printf '\n' + return 0 + fi + + if [[ "$value" =~ ^0x[0-9A-Fa-f]{64}$ ]]; then + printf '0x%s\n' "$(printf '%s' "${value#0x}" | tr '[:upper:]' '[:lower:]')" + return 0 + fi + + if [[ "$value" =~ ^[0-9A-Fa-f]{64}$ ]]; then + printf '0x%s\n' "$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')" + return 0 + fi + + echo "Invalid seed for $name. Expected 64 hex bytes with optional 0x prefix." >&2 + exit 1 +} + +write_secret_file() { + local file="$1" + local value="$2" + + mkdir -p "$(dirname "$file")" + printf '%s\n' "$value" > "$file" + chmod 600 "$file" +} + +generate_seed() { + printf '0x%s\n' "$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n' | tr '[:upper:]' '[:lower:]')" +} + +resolve_seed() { + local env_name="$1" + local file_name="$2" + local file="$SEED_DIR/$file_name" + local raw_value normalized source + + raw_value=$(trim_whitespace "${!env_name:-}") + if [ -n "$raw_value" ]; then + normalized=$(canonicalize_seed "$env_name" "$raw_value") + source="env" + write_secret_file "$file" "$normalized" + elif [ -f "$file" ]; then + normalized=$(canonicalize_seed "$env_name" "$(cat "$file")") + source="persisted" + write_secret_file "$file" "$normalized" + else + normalized=$(generate_seed) + source="generated" + write_secret_file "$file" "$normalized" + fi + + printf '%s|%s\n' "$normalized" "$source" +} + +resolve_optional_seed() { + local env_name="$1" + local file_name="$2" + local file="$SEED_DIR/$file_name" + local raw_value normalized source + + raw_value=$(trim_whitespace "${!env_name:-}") + if [ -n "$raw_value" ]; then + normalized=$(canonicalize_seed "$env_name" "$raw_value") + source="env" + write_secret_file "$file" "$normalized" + elif [ -f "$file" ]; then + normalized=$(canonicalize_seed "$env_name" "$(cat "$file")") + source="persisted" + write_secret_file "$file" "$normalized" + else + normalized="" + source="shared-v1" + fi + + printf '%s|%s\n' "$normalized" "$source" +} + +read_seed_result() { + local var_prefix="$1" + local result="$2" + local seed="${result%%|*}" + local source="${result##*|}" + + printf -v "${var_prefix}_SEED" '%s' "$seed" + printf -v "${var_prefix}_SOURCE" '%s' "$source" +} + +ensure_node_key_file() { + local name="$1" + local file="$NODE_KEY_DIR/${name}.key" + + if [ ! -s "$file" ]; then + mkdir -p "$NODE_KEY_DIR" + "$NODE_BIN" key generate-node-key --chain local > "$file" + chmod 600 "$file" + fi + + printf '%s\n' "$file" +} + derive_address() { local suri="$1" + local scheme="${2:-Sr25519}" - "$NODE_BIN" key inspect --scheme Sr25519 "$suri" --output-type json \ + "$NODE_BIN" key inspect --scheme "$scheme" "$suri" --output-type json \ | grep -o '"ss58Address": "[^"]*"' \ | cut -d'"' -f4 } @@ -70,22 +184,107 @@ start_logged() { PIDS+=("$!") } -V1_ADDR=$(derive_address "$v1sec") -V2_ADDR=$(derive_address "$v2sec") -V3_ADDR=$(derive_address "$v3sec") +write_cluster_info() { + python3 - "$CLUSTER_INFO_FILE" \ + "$V1_ADDR" "$V1_SOURCE" "$VALIDATOR1_P2P_PORT" \ + "$V2_ADDR" "$V2_SOURCE" "$VALIDATOR2_P2P_PORT" \ + "$V3_ADDR" "$V3_SOURCE" "$VALIDATOR3_P2P_PORT" \ + "$FAUCET_ADDR" "$FAUCET_SOURCE" "$FAUCET_PORT" \ + "$BOOTNODE_PEER_ID" "$BOOTNODE_P2P_PORT" \ + "$RPC_NODE_P2P_PORT" "$RPC_NODE_WS_PORT" \ + "$SPEC_HTTP_PORT" "$STATE_DIR" <<'PY' +import json +import sys + +output_file = sys.argv[1] + +cluster_info = { + "validators": [ + { + "name": "Validator1", + "ss58Address": sys.argv[2], + "seedSource": sys.argv[3], + "p2pPort": int(sys.argv[4]), + }, + { + "name": "Validator2", + "ss58Address": sys.argv[5], + "seedSource": sys.argv[6], + "p2pPort": int(sys.argv[7]), + }, + { + "name": "Validator3", + "ss58Address": sys.argv[8], + "seedSource": sys.argv[9], + "p2pPort": int(sys.argv[10]), + }, + ], + "faucet": { + "ss58Address": sys.argv[11], + "seedSource": sys.argv[12], + "httpPort": int(sys.argv[13]), + }, + "bootnodePeerId": sys.argv[14], + "ports": { + "bootnodeP2P": int(sys.argv[15]), + "rpcNodeP2P": int(sys.argv[16]), + "rpcWs": int(sys.argv[17]), + "specHttp": int(sys.argv[18]), + }, + "statePath": sys.argv[19], +} -HELPER_DIR="$WORK_DIR/spec-generator" -FAUCET_DIR="/workspace/faucet" +with open(output_file, "w", encoding="utf-8") as handle: + json.dump(cluster_info, handle, indent=2) +PY +} -rm -rf "$WORK_DIR" "$OUTPUT_DIR" /tmp/validator1 /tmp/validator2 /tmp/validator3 /tmp/rpc-node /tmp/bootnode -mkdir -p "$HELPER_DIR" "$OUTPUT_DIR" "$FAUCET_DIR" +mkdir -p "$STATE_DIR" "$SEED_DIR" "$NODE_KEY_DIR" "$OUTPUT_DIR" +chmod 700 "$STATE_DIR" "$SEED_DIR" "$NODE_KEY_DIR" -echo "Downloading self-contained cluster helpers..." -wget -q -O "$HELPER_DIR/update-spec.py" \ - "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.py" -wget -q -O "$HELPER_DIR/update-spec.sh" \ - "https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/${SCRIPTS_COMMIT}/spec-generator/scripts/update-spec.sh" -chmod +x "$HELPER_DIR/update-spec.sh" +read_seed_result "V1" "$(resolve_seed v1sec v1sec)" +read_seed_result "V2" "$(resolve_seed v2sec v2sec)" +read_seed_result "V3" "$(resolve_seed v3sec v3sec)" +read_seed_result "FAUCET_OVERRIDE" "$(resolve_optional_seed faucetsec faucetsec)" + +if [ -n "$FAUCET_OVERRIDE_SEED" ]; then + FAUCET_SEED="$FAUCET_OVERRIDE_SEED" + FAUCET_SOURCE="$FAUCET_OVERRIDE_SOURCE" +else + FAUCET_SEED="$V1_SEED" + FAUCET_SOURCE="shared-v1" +fi + +V1_ADDR=$(derive_address "$V1_SEED") +V1_BABE=$(derive_address "$V1_SEED//babe") +V1_GRAN=$(derive_address "$V1_SEED//grandpa" Ed25519) +V1_IMON=$(derive_address "$V1_SEED//im_online") +V1_AUDI=$(derive_address "$V1_SEED//authority_discovery") + +V2_ADDR=$(derive_address "$V2_SEED") +V2_BABE=$(derive_address "$V2_SEED//babe") +V2_GRAN=$(derive_address "$V2_SEED//grandpa" Ed25519) +V2_IMON=$(derive_address "$V2_SEED//im_online") +V2_AUDI=$(derive_address "$V2_SEED//authority_discovery") + +V3_ADDR=$(derive_address "$V3_SEED") +V3_BABE=$(derive_address "$V3_SEED//babe") +V3_GRAN=$(derive_address "$V3_SEED//grandpa" Ed25519) +V3_IMON=$(derive_address "$V3_SEED//im_online") +V3_AUDI=$(derive_address "$V3_SEED//authority_discovery") + +FAUCET_ADDR=$(derive_address "$FAUCET_SEED") + +rm -rf "$WORK_DIR" /tmp/validator1 /tmp/validator2 /tmp/validator3 /tmp/rpc-node /tmp/bootnode "$FAUCET_DIR" +mkdir -p "$WORK_DIR" "$FAUCET_DIR" +find "$OUTPUT_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null || true + +echo "Resolved validator addresses:" +echo " - Validator1: $V1_ADDR ($V1_SOURCE)" +echo " - Validator2: $V2_ADDR ($V2_SOURCE)" +echo " - Validator3: $V3_ADDR ($V3_SOURCE)" +echo " - Faucet: $FAUCET_ADDR ($FAUCET_SOURCE)" +echo "Private state directory: $STATE_DIR" echo "Downloading local faucet sources..." wget -q -O "$FAUCET_DIR/package.json" \ @@ -96,36 +295,109 @@ wget -q -O "$FAUCET_DIR/server.js" \ echo "Installing faucet dependencies..." ( cd "$FAUCET_DIR" - npm install --omit=dev + npm install --omit=dev --no-fund --no-audit ) echo "Generating chain spec..." "$NODE_BIN" build-spec --chain "$CHAIN_TEMPLATE" --disable-default-bootnode > "$PLAIN_SPEC" -"$HELPER_DIR/update-spec.sh" \ - --v1_addr "$V1_ADDR" --v1_sec "$v1sec" \ - --v2_addr "$V2_ADDR" --v2_sec "$v2sec" \ - --v3_addr "$V3_ADDR" --v3_sec "$v3sec" \ - --input "$PLAIN_SPEC" \ - --output "$UPDATED_SPEC" +python3 - "$PLAIN_SPEC" "$UPDATED_SPEC" \ + "$V1_ADDR" "$V1_BABE" "$V1_GRAN" "$V1_IMON" "$V1_AUDI" \ + "$V2_ADDR" "$V2_BABE" "$V2_GRAN" "$V2_IMON" "$V2_AUDI" \ + "$V3_ADDR" "$V3_BABE" "$V3_GRAN" "$V3_IMON" "$V3_AUDI" \ + "$FAUCET_ADDR" "$V1_ADDR" <<'PY' +import json +import sys + +input_file = sys.argv[1] +output_file = sys.argv[2] + +v1_addr, v1_babe, v1_gran, v1_imon, v1_audi = sys.argv[3:8] +v2_addr, v2_babe, v2_gran, v2_imon, v2_audi = sys.argv[8:13] +v3_addr, v3_babe, v3_gran, v3_imon, v3_audi = sys.argv[13:18] +faucet_addr = sys.argv[18] +default_faucet_addr = sys.argv[19] + +AMOUNT = 100000000000000000000000000 +STAKE = 1000000000000000000000000 + +with open(input_file, "r", encoding="utf-8") as handle: + spec = json.load(handle) + +balances = spec["genesis"]["runtimeGenesis"]["patch"]["balances"]["balances"] + +def upsert_balance(address, amount): + for entry in balances: + if entry[0] == address: + entry[1] = max(int(entry[1]), amount) + return + balances.append([address, amount]) + +for address in (v1_addr, v2_addr, v3_addr): + upsert_balance(address, AMOUNT) + +if faucet_addr != default_faucet_addr: + upsert_balance(faucet_addr, AMOUNT) + +spec["genesis"]["runtimeGenesis"]["patch"]["session"]["keys"] = [ + [ + v1_addr, + v1_addr, + { + "authority_discovery": v1_audi, + "babe": v1_babe, + "grandpa": v1_gran, + "im_online": v1_imon, + }, + ], + [ + v2_addr, + v2_addr, + { + "authority_discovery": v2_audi, + "babe": v2_babe, + "grandpa": v2_gran, + "im_online": v2_imon, + }, + ], + [ + v3_addr, + v3_addr, + { + "authority_discovery": v3_audi, + "babe": v3_babe, + "grandpa": v3_gran, + "im_online": v3_imon, + }, + ], +] + +spec["genesis"]["runtimeGenesis"]["patch"]["staking"]["invulnerables"] = [ + v1_addr, + v2_addr, + v3_addr, +] + +spec["genesis"]["runtimeGenesis"]["patch"]["staking"]["stakers"] = [ + [v1_addr, v1_addr, STAKE, "Validator"], + [v2_addr, v2_addr, STAKE, "Validator"], + [v3_addr, v3_addr, STAKE, "Validator"], +] + +with open(output_file, "w", encoding="utf-8") as handle: + json.dump(spec, handle, indent=2) +PY "$NODE_BIN" build-spec \ --chain "$UPDATED_SPEC" \ --disable-default-bootnode \ --raw > "$SPEC_FILE" -echo "Generating node identities..." -BOOTNODE_NODE_KEY_FILE="$WORK_DIR/bootnode_node_key.txt" -V1_NODE_KEY_FILE="$WORK_DIR/v1_node_key.txt" -V2_NODE_KEY_FILE="$WORK_DIR/v2_node_key.txt" -V3_NODE_KEY_FILE="$WORK_DIR/v3_node_key.txt" -RPC_NODE_KEY_FILE="$WORK_DIR/rpc_node_key.txt" - -"$NODE_BIN" key generate-node-key --chain local > "$BOOTNODE_NODE_KEY_FILE" -"$NODE_BIN" key generate-node-key --chain local > "$V1_NODE_KEY_FILE" -"$NODE_BIN" key generate-node-key --chain local > "$V2_NODE_KEY_FILE" -"$NODE_BIN" key generate-node-key --chain local > "$V3_NODE_KEY_FILE" -"$NODE_BIN" key generate-node-key --chain local > "$RPC_NODE_KEY_FILE" +BOOTNODE_NODE_KEY_FILE=$(ensure_node_key_file bootnode) +V1_NODE_KEY_FILE=$(ensure_node_key_file validator1) +V2_NODE_KEY_FILE=$(ensure_node_key_file validator2) +V3_NODE_KEY_FILE=$(ensure_node_key_file validator3) +RPC_NODE_KEY_FILE=$(ensure_node_key_file rpc-node) BOOTNODE_PEER_ID=$("$NODE_BIN" key inspect-node-key --file "$BOOTNODE_NODE_KEY_FILE" 2>/dev/null | tail -n1 | tr -d '\r') if [ -z "$BOOTNODE_PEER_ID" ]; then @@ -133,23 +405,27 @@ if [ -z "$BOOTNODE_PEER_ID" ]; then exit 1 fi -printf '%s\n' "$BOOTNODE_PEER_ID" > "$OUTPUT_DIR/bootnode_node_key.txt" -printf '%s\n' "$BOOTNODE_PEER_ID" > "$OUTPUT_DIR/bootnode_peer_id.txt" +printf '%s\n' "$BOOTNODE_PEER_ID" > "$BOOTNODE_PEER_ID_FILE" +printf '%s\n' "$BOOTNODE_PEER_ID" > "$BOOTNODE_COMPAT_FILE" +write_cluster_info + +echo "Bootnode peer ID: $BOOTNODE_PEER_ID" -insert_keys /tmp/validator1 "$v1sec" -insert_keys /tmp/validator2 "$v2sec" -insert_keys /tmp/validator3 "$v3sec" +insert_keys /tmp/validator1 "$V1_SEED" +insert_keys /tmp/validator2 "$V2_SEED" +insert_keys /tmp/validator3 "$V3_SEED" python3 -m http.server "$SPEC_HTTP_PORT" --bind 0.0.0.0 --directory "$OUTPUT_DIR" >/tmp/spec-server.log 2>&1 & PIDS+=("$!") -BOOTNODE_MULTIADDR="/ip4/127.0.0.1/tcp/30335/p2p/$BOOTNODE_PEER_ID" +BOOTNODE_MULTIADDR="/ip4/127.0.0.1/tcp/${BOOTNODE_P2P_PORT}/p2p/${BOOTNODE_PEER_ID}" start_logged /tmp/bootnode.log \ "$NODE_BIN" \ --base-path /tmp/bootnode \ --chain "$SPEC_FILE" \ - --port 30335 \ + --port "$BOOTNODE_P2P_PORT" \ + --prometheus-port "$BOOTNODE_PROMETHEUS_PORT" \ --node-key-file "$BOOTNODE_NODE_KEY_FILE" \ --name Bootnode \ --no-telemetry @@ -158,7 +434,8 @@ start_logged /tmp/validator1.log \ "$NODE_BIN" \ --base-path /tmp/validator1 \ --chain "$SPEC_FILE" \ - --port 30333 \ + --port "$VALIDATOR1_P2P_PORT" \ + --prometheus-port "$VALIDATOR1_PROMETHEUS_PORT" \ --node-key-file "$V1_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -169,7 +446,8 @@ start_logged /tmp/validator2.log \ "$NODE_BIN" \ --base-path /tmp/validator2 \ --chain "$SPEC_FILE" \ - --port 30334 \ + --port "$VALIDATOR2_P2P_PORT" \ + --prometheus-port "$VALIDATOR2_PROMETHEUS_PORT" \ --node-key-file "$V2_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -180,7 +458,8 @@ start_logged /tmp/validator3.log \ "$NODE_BIN" \ --base-path /tmp/validator3 \ --chain "$SPEC_FILE" \ - --port 30336 \ + --port "$VALIDATOR3_P2P_PORT" \ + --prometheus-port "$VALIDATOR3_PROMETHEUS_PORT" \ --node-key-file "$V3_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -192,6 +471,7 @@ start_logged /tmp/rpc-node.log \ --base-path /tmp/rpc-node \ --chain "$SPEC_FILE" \ --port "$RPC_NODE_P2P_PORT" \ + --prometheus-port "$RPC_NODE_PROMETHEUS_PORT" \ --node-key-file "$RPC_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --rpc-external \ diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index fc2f55bcc..796588bda 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -1,8 +1,10 @@ [variables] +# Optional advanced overrides. Leave blank to auto-generate and persist validator identities. v1sec = "" v2sec = "" v3sec = "" +# Optional dedicated faucet seed. Leave blank to reuse validator 1. faucetsec = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" @@ -50,10 +52,12 @@ host = "faucet.${domain}" path = "/" [config.env] +# Optional advanced overrides. Leave blank to auto-generate and persist validator identities. v1sec = "" v2sec = "" v3sec = "" +# Optional dedicated faucet seed. Leave blank to reuse validator 1. faucetsec = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" From 77cbb629124dad40b57dba8f41229fe685c46606 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 19:56:05 +0530 Subject: [PATCH 75/90] fix: pin reef dev cluster bootstrap --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- blueprints/reef-dev-cluster/run-cluster.sh | 0 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 blueprints/reef-dev-cluster/run-cluster.sh diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 20b339cc3..52886cc73 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -44,13 +44,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/02da0d9fd6f8d88cb949d38eb8af8adca56b10bf/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/def2dca54efbc522d383d3aa67528a121233b9bc/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=02da0d9fd6f8d88cb949d38eb8af8adca56b10bf /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=def2dca54efbc522d383d3aa67528a121233b9bc /workspace/run-cluster.sh volumes: reef-dev-cluster-state: diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh old mode 100644 new mode 100755 From fa4925ae5502119835be95abf67fc22cef429f83 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:20:46 +0530 Subject: [PATCH 76/90] feat: add eth rpc to reef dev cluster --- .../reef-dev-cluster/docker-compose.yml | 21 ++++++++++++++++++ blueprints/reef-dev-cluster/faucet/server.js | 22 +++++++++++++++++++ blueprints/reef-dev-cluster/run-cluster.sh | 8 ++++--- blueprints/reef-dev-cluster/template.toml | 6 +++++ meta.json | 2 +- 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 52886cc73..96cc01bca 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -25,6 +25,7 @@ services: - "30336" - "8001" - "9944" + - "8545" - "8080" entrypoint: /bin/sh @@ -52,6 +53,26 @@ services: echo '🚀 Starting self-contained network...' exec env TEMPLATE_ASSET_REF=def2dca54efbc522d383d3aa67528a121233b9bc /workspace/run-cluster.sh + reef-dev-cluster-eth-rpc: + image: anukulpandey/reef-chain-eth-rpc:latest + restart: unless-stopped + working_dir: /workspace + depends_on: + - reef-dev-cluster + network_mode: "service:reef-dev-cluster" + environment: + NODE_RPC_URL: ws://127.0.0.1:9944 + PORT: "8545" + entrypoint: /bin/sh + command: + - -c + - | + set -e + until eth-rpc --node-rpc-url "$$NODE_RPC_URL" --rpc-port "$$PORT" --rpc-external --rpc-cors all; do + echo 'eth-rpc is waiting for the ws rpc node...' + sleep 2 + done + volumes: reef-dev-cluster-state: reef-dev-cluster-download: diff --git a/blueprints/reef-dev-cluster/faucet/server.js b/blueprints/reef-dev-cluster/faucet/server.js index ad53d53b9..3e88ade38 100644 --- a/blueprints/reef-dev-cluster/faucet/server.js +++ b/blueprints/reef-dev-cluster/faucet/server.js @@ -248,6 +248,28 @@ function enqueue(task) { const server = http.createServer(async (request, response) => { try { + if (request.method === "GET" && request.url === "/") { + jsonResponse(response, 200, { + service: "reef-dev-cluster-faucet", + status: "ok", + endpoints: { + health: { + method: "GET", + path: "/health", + }, + drip: { + method: "POST", + path: "/drip", + body: { + to: "0x0000000000000000000000000000000000000000", + amount: config.defaultAmount, + }, + }, + }, + }); + return; + } + if (request.method === "GET" && request.url === "/health") { jsonResponse(response, 200, { status: "ok" }); return; diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index b7772f004..1032f9875 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -19,6 +19,7 @@ VALIDATOR2_P2P_PORT="${VALIDATOR2_P2P_PORT:-30334}" VALIDATOR3_P2P_PORT="${VALIDATOR3_P2P_PORT:-30336}" RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" +ETH_RPC_PORT="${ETH_RPC_PORT:-8545}" FAUCET_PORT="${FAUCET_PORT:-8080}" BOOTNODE_PROMETHEUS_PORT="${BOOTNODE_PROMETHEUS_PORT:-9615}" VALIDATOR1_PROMETHEUS_PORT="${VALIDATOR1_PROMETHEUS_PORT:-9616}" @@ -191,7 +192,7 @@ write_cluster_info() { "$V3_ADDR" "$V3_SOURCE" "$VALIDATOR3_P2P_PORT" \ "$FAUCET_ADDR" "$FAUCET_SOURCE" "$FAUCET_PORT" \ "$BOOTNODE_PEER_ID" "$BOOTNODE_P2P_PORT" \ - "$RPC_NODE_P2P_PORT" "$RPC_NODE_WS_PORT" \ + "$RPC_NODE_P2P_PORT" "$RPC_NODE_WS_PORT" "$ETH_RPC_PORT" \ "$SPEC_HTTP_PORT" "$STATE_DIR" <<'PY' import json import sys @@ -229,9 +230,10 @@ cluster_info = { "bootnodeP2P": int(sys.argv[15]), "rpcNodeP2P": int(sys.argv[16]), "rpcWs": int(sys.argv[17]), - "specHttp": int(sys.argv[18]), + "ethRpcHttp": int(sys.argv[18]), + "specHttp": int(sys.argv[19]), }, - "statePath": sys.argv[19], + "statePath": sys.argv[20], } with open(output_file, "w", encoding="utf-8") as handle: diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index 796588bda..fd012ffa9 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -45,6 +45,12 @@ port = 9944 host = "ws.${domain}" path = "/" +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8545 +host = "eth.${domain}" +path = "/" + [[config.domains]] serviceName = "reef-dev-cluster" port = 8080 diff --git a/meta.json b/meta.json index 9030e96fa..51914512e 100644 --- a/meta.json +++ b/meta.json @@ -5449,7 +5449,7 @@ "id": "reef-dev-cluster", "name": "Reef Chain - Dev Cluster", "version": "1", - "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, and faucet", + "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, EVM RPC, and faucet", "links": { "github": "https://github.com/reef-chain/chain-upgrade", "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", From 3b65138b19693905b66ca5844b959e889d40fdcd Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:22:28 +0530 Subject: [PATCH 77/90] fix: pin reef dev cluster eth rpc assets --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 96cc01bca..2bc066df3 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -45,13 +45,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/def2dca54efbc522d383d3aa67528a121233b9bc/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/fa4925ae5502119835be95abf67fc22cef429f83/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=def2dca54efbc522d383d3aa67528a121233b9bc /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=fa4925ae5502119835be95abf67fc22cef429f83 /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest From 86bdfc6d2646e38c84fcbff63b8af35454404ee7 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:38:59 +0530 Subject: [PATCH 78/90] fix: align reef traefik routing --- blueprints/reef-bootnode/template.toml | 4 +- blueprints/reef-dev-cluster/run-cluster.sh | 10 ++--- blueprints/reef-dev-cluster/template.toml | 20 +-------- blueprints/reef-eth-rpc/docker-compose.yml | 2 +- blueprints/reef-eth-rpc/template.toml | 10 ++++- blueprints/reef-faucet/template.toml | 14 ++++-- blueprints/reef-rpc/docker-compose.yml | 52 ++++++++++++++++------ blueprints/reef-rpc/template.toml | 14 +++--- blueprints/reef-spec-gen/template.toml | 6 +++ blueprints/reef-validator/template.toml | 14 ++---- 10 files changed, 83 insertions(+), 63 deletions(-) diff --git a/blueprints/reef-bootnode/template.toml b/blueprints/reef-bootnode/template.toml index 58605f0de..9907faa74 100644 --- a/blueprints/reef-bootnode/template.toml +++ b/blueprints/reef-bootnode/template.toml @@ -13,14 +13,14 @@ P2P_PORT="30335" [[config.domains]] serviceName = "reef-bootnode" -port = 30335 +port = 8000 host = "${domain}" path = "/" [[config.domains]] serviceName = "reef-bootnode" port = 8000 -host = "${domain}" +host = "spec.${domain}" path = "/" [config.env] diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 1032f9875..22d4039e6 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -428,6 +428,7 @@ start_logged /tmp/bootnode.log \ --chain "$SPEC_FILE" \ --port "$BOOTNODE_P2P_PORT" \ --prometheus-port "$BOOTNODE_PROMETHEUS_PORT" \ + --rpc-port 0 \ --node-key-file "$BOOTNODE_NODE_KEY_FILE" \ --name Bootnode \ --no-telemetry @@ -438,6 +439,7 @@ start_logged /tmp/validator1.log \ --chain "$SPEC_FILE" \ --port "$VALIDATOR1_P2P_PORT" \ --prometheus-port "$VALIDATOR1_PROMETHEUS_PORT" \ + --rpc-port 0 \ --node-key-file "$V1_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -450,6 +452,7 @@ start_logged /tmp/validator2.log \ --chain "$SPEC_FILE" \ --port "$VALIDATOR2_P2P_PORT" \ --prometheus-port "$VALIDATOR2_PROMETHEUS_PORT" \ + --rpc-port 0 \ --node-key-file "$V2_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -462,6 +465,7 @@ start_logged /tmp/validator3.log \ --chain "$SPEC_FILE" \ --port "$VALIDATOR3_P2P_PORT" \ --prometheus-port "$VALIDATOR3_PROMETHEUS_PORT" \ + --rpc-port 0 \ --node-key-file "$V3_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ --validator \ @@ -476,11 +480,7 @@ start_logged /tmp/rpc-node.log \ --prometheus-port "$RPC_NODE_PROMETHEUS_PORT" \ --node-key-file "$RPC_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ - --rpc-external \ - --rpc-port "$RPC_NODE_WS_PORT" \ - --rpc-cors all \ - --rpc-methods Unsafe \ - --rpc-max-connections 10000 \ + --experimental-rpc-endpoint "listen-addr=0.0.0.0:${RPC_NODE_WS_PORT},cors=all,methods=unsafe,max-connections=10000" \ --pruning archive \ --name rpc-node \ --no-telemetry diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index fd012ffa9..e2bbe4698 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -11,25 +11,7 @@ MAX_AMOUNT = "10" [[config.domains]] serviceName = "reef-dev-cluster" -port = 30335 -host = "${domain}" -path = "/" - -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 30334 -host = "${domain}" -path = "/" - -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 30333 -host = "${domain}" -path = "/" - -[[config.domains]] -serviceName = "reef-dev-cluster" -port = 30336 +port = 8001 host = "${domain}" path = "/" diff --git a/blueprints/reef-eth-rpc/docker-compose.yml b/blueprints/reef-eth-rpc/docker-compose.yml index d13caf3b1..57ae2b8be 100644 --- a/blueprints/reef-eth-rpc/docker-compose.yml +++ b/blueprints/reef-eth-rpc/docker-compose.yml @@ -13,4 +13,4 @@ services: expose: - "${PORT}" entrypoint: /bin/sh - command: -c "eth-rpc --node-rpc-url $NODE_RPC_URL --rpc-port $PORT --rpc-external --rpc-cors all" + command: -c "eth-rpc --node-rpc-url $$NODE_RPC_URL --rpc-port $$PORT --rpc-external --rpc-cors all" diff --git a/blueprints/reef-eth-rpc/template.toml b/blueprints/reef-eth-rpc/template.toml index 15b02a3c3..c611c4d1b 100644 --- a/blueprints/reef-eth-rpc/template.toml +++ b/blueprints/reef-eth-rpc/template.toml @@ -1,6 +1,6 @@ [variables] PORT = "8545" -NODE_RPC_URL = "ws://reef.host:9944" +NODE_RPC_URL = "ws://ws.reef.host" [[config.domains]] serviceName = "reef-eth-rpc" @@ -8,6 +8,12 @@ port = 8545 host = "${domain}" path = "/" +[[config.domains]] +serviceName = "reef-eth-rpc" +port = 8545 +host = "eth.${domain}" +path = "/" + [config.env] PORT = "8545" -NODE_RPC_URL = "ws://reef.host:9944" \ No newline at end of file +NODE_RPC_URL = "ws://ws.reef.host" diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml index 36b855ed5..073ad4c95 100644 --- a/blueprints/reef-faucet/template.toml +++ b/blueprints/reef-faucet/template.toml @@ -1,7 +1,7 @@ [variables] PORT = "8080" -WS_ENDPOINT = "ws://reef.host:9944" -EVM_RPC_URL = "http://reef.host:8545" +WS_ENDPOINT = "ws://ws.reef.host" +EVM_RPC_URL = "http://eth.reef.host" FAUCET_SEED = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" @@ -12,10 +12,16 @@ port = 8080 host = "${domain}" path = "/" +[[config.domains]] +serviceName = "reef-faucet" +port = 8080 +host = "faucet.${domain}" +path = "/" + [config.env] PORT = "8080" -WS_ENDPOINT = "ws://reef.host:9944" -EVM_RPC_URL = "http://reef.host:8545" +WS_ENDPOINT = "ws://ws.reef.host" +EVM_RPC_URL = "http://eth.reef.host" FAUCET_SEED = "" DEFAULT_AMOUNT = "1" MAX_AMOUNT = "10" diff --git a/blueprints/reef-rpc/docker-compose.yml b/blueprints/reef-rpc/docker-compose.yml index 4f7fdb1d2..2ea58ecb3 100644 --- a/blueprints/reef-rpc/docker-compose.yml +++ b/blueprints/reef-rpc/docker-compose.yml @@ -21,20 +21,46 @@ services: - ./output:/output entrypoint: /bin/sh - command: -c " - set -e && + command: + - -c + - | + set -e - echo '📦 Installing dependencies...' && - apt-get update && - apt-get install -y wget ca-certificates && + echo '📦 Installing dependencies...' + apt-get update + apt-get install -y wget ca-certificates - echo '📥 Downloading start-rpc.sh...' && - mkdir -p /workspace/rpc/scripts && - wget -O /workspace/rpc/scripts/start-rpc.sh \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/rpc/scripts/start-rpc.sh && + RPC_BASE_PATH=/tmp/rpc-node + NODE_KEY_FILE=/tmp/rpc_node_key.txt + CHAIN_FILE=/tmp/local-chain-spec-raw.json + PEER_ID_FILE=/tmp/bootnode_peer_id.txt - echo '🔧 Making script executable...' && - chmod +x /workspace/rpc/scripts/start-rpc.sh && + echo '📥 Downloading chain spec and bootnode peer ID...' + wget -q -O "$$CHAIN_FILE" "$$SPEC_URL" + wget -q -O "$$PEER_ID_FILE" "$$BOOTNODE_NODE_KEY" - echo '🚀 Starting RPC' && - exec /workspace/rpc/scripts/start-rpc.sh" + BOOTNODE_PEER_ID=$$(tr -d '\r\n' < "$$PEER_ID_FILE") + if [ -z "$$BOOTNODE_PEER_ID" ]; then + echo 'Bootnode peer ID file is empty' >&2 + exit 1 + fi + + BOOTNODE_MULTIADDR="/ip4/$$BOOTNODE_IP/tcp/30335/p2p/$$BOOTNODE_PEER_ID" + + mkdir -p "$$RPC_BASE_PATH" + chmod 700 "$$RPC_BASE_PATH" + + reef-node key generate-node-key --chain local > "$$NODE_KEY_FILE" + chmod 600 "$$NODE_KEY_FILE" + + echo '🚀 Starting RPC node...' + exec reef-node \ + --base-path "$$RPC_BASE_PATH" \ + --chain "$$CHAIN_FILE" \ + --port "$$P2P_PORT" \ + --node-key-file "$$NODE_KEY_FILE" \ + --bootnodes "$$BOOTNODE_MULTIADDR" \ + --experimental-rpc-endpoint "listen-addr=0.0.0.0:$$RPC_PORT,cors=all,methods=unsafe,max-connections=10000" \ + --pruning archive \ + --name rpc-node \ + --no-telemetry diff --git a/blueprints/reef-rpc/template.toml b/blueprints/reef-rpc/template.toml index c87ec8ade..c146b6a27 100644 --- a/blueprints/reef-rpc/template.toml +++ b/blueprints/reef-rpc/template.toml @@ -1,6 +1,6 @@ [variables] -SPEC_URL = "http://reef.host:8001/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" +SPEC_URL = "http://spec.reef.host/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://spec.reef.host/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" @@ -9,21 +9,21 @@ RPC_PORT="9994" [[config.domains]] serviceName = "reef-rpc" -port = 30339 +port = 9994 host = "${domain}" path = "/" [[config.domains]] serviceName = "reef-rpc" port = 9994 -host = "${domain}" +host = "ws.${domain}" path = "/" [config.env] -SPEC_URL = "http://reef.host:8001/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8001/bootnode_node_key.txt" +SPEC_URL = "http://spec.reef.host/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://spec.reef.host/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" P2P_PORT="30339" -RPC_PORT="9994" \ No newline at end of file +RPC_PORT="9994" diff --git a/blueprints/reef-spec-gen/template.toml b/blueprints/reef-spec-gen/template.toml index 86034cbde..c0412908f 100644 --- a/blueprints/reef-spec-gen/template.toml +++ b/blueprints/reef-spec-gen/template.toml @@ -1,5 +1,11 @@ [variables] +[[config.domains]] +serviceName = "reef-spec-gen" +port = 8000 +host = "${domain}" +path = "/" + [[config.domains]] serviceName = "reef-spec-gen" port = 8000 diff --git a/blueprints/reef-validator/template.toml b/blueprints/reef-validator/template.toml index 6b3f1a55b..c5e06fcf9 100644 --- a/blueprints/reef-validator/template.toml +++ b/blueprints/reef-validator/template.toml @@ -2,8 +2,8 @@ V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" V1SEED = "" -SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" +SPEC_URL = "http://spec.reef.host/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://spec.reef.host/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" @@ -11,18 +11,12 @@ VALIDATOR_NAME="Validator-1" P2P_PORT="30333" -[[config.domains]] -serviceName = "reef-validator" -port = 30333 -host = "${domain}" -path = "/" - [config.env] V1ADDR = "5GH14oJ4A3VDLoC6nXutsTwS4dMwebDipNdSugD5zFnnWKp5" V1SEED = "" -SPEC_URL = "http://reef.host:8000/local-chain-spec-raw.json" -BOOTNODE_NODE_KEY="http://reef.host:8000/bootnode_node_key.txt" +SPEC_URL = "http://spec.reef.host/local-chain-spec-raw.json" +BOOTNODE_NODE_KEY="http://spec.reef.host/bootnode_node_key.txt" BOOTNODE_IP="72.60.35.83" From a39607789f98b55fcbe30c7c8d9320719d4947fa Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:39:24 +0530 Subject: [PATCH 79/90] fix: pin reef dev cluster traefik updates --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 2bc066df3..15efd83d0 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -45,13 +45,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/fa4925ae5502119835be95abf67fc22cef429f83/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/86bdfc6d2646e38c84fcbff63b8af35454404ee7/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=fa4925ae5502119835be95abf67fc22cef429f83 /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=86bdfc6d2646e38c84fcbff63b8af35454404ee7 /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest From 182735be1afc163b0e99f28871ec5b6cb3c03cc3 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:46:08 +0530 Subject: [PATCH 80/90] feat: add reef faucet frontend --- blueprints/reef-dev-cluster/faucet/server.js | 437 +++++++++++- blueprints/reef-dev-cluster/run-cluster.sh | 14 +- blueprints/reef-faucet/faucet/package.json | 13 + blueprints/reef-faucet/faucet/server.js | 696 +++++++++++++++++++ 4 files changed, 1136 insertions(+), 24 deletions(-) create mode 100644 blueprints/reef-faucet/faucet/package.json create mode 100644 blueprints/reef-faucet/faucet/server.js diff --git a/blueprints/reef-dev-cluster/faucet/server.js b/blueprints/reef-dev-cluster/faucet/server.js index 3e88ade38..957bae12c 100644 --- a/blueprints/reef-dev-cluster/faucet/server.js +++ b/blueprints/reef-dev-cluster/faucet/server.js @@ -5,10 +5,12 @@ const { ApiPromise, Keyring, WsProvider } = require("@polkadot/api"); const config = { wsEndpoint: process.env.WS_ENDPOINT || "ws://127.0.0.1:9944", + evmRpcUrl: process.env.EVM_RPC_URL || "", faucetSeed: process.env.FAUCET_SEED || "", port: Number.parseInt(process.env.PORT || "8080", 10), defaultAmount: String(process.env.DEFAULT_AMOUNT || "1"), maxAmount: String(process.env.MAX_AMOUNT || "10"), + serviceName: process.env.SERVICE_NAME || "reef-faucet", }; if (!config.faucetSeed) { @@ -176,6 +178,412 @@ function jsonResponse(response, statusCode, body) { response.end(JSON.stringify(body)); } +function htmlResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" }); + response.end(body); +} + +function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function wantsHtml(request) { + const accept = String(request.headers.accept || ""); + return accept.includes("text/html"); +} + +function buildInfoPayload() { + return { + service: config.serviceName, + status: "ok", + defaults: { + defaultAmount: config.defaultAmount, + maxAmount: config.maxAmount, + }, + upstream: { + wsEndpoint: config.wsEndpoint, + evmRpcUrl: config.evmRpcUrl || null, + }, + endpoints: { + health: { + method: "GET", + path: "/health", + }, + drip: { + method: "POST", + path: "/drip", + body: { + to: "0x0000000000000000000000000000000000000000", + amount: config.defaultAmount, + }, + }, + }, + }; +} + +function buildFrontendHtml() { + const bootPayload = JSON.stringify(buildInfoPayload()); + const title = escapeHtml(config.serviceName); + const defaultAmount = escapeHtml(config.defaultAmount); + const maxAmount = escapeHtml(config.maxAmount); + const wsEndpoint = escapeHtml(config.wsEndpoint); + const evmRpcUrl = escapeHtml(config.evmRpcUrl || "Not configured"); + + return ` + + + + + ${title} + + + +

+
+
+

Reef Faucet

+

${title}

+

Send a small amount of test REEF to any EVM address without reaching for curl first.

+
+ +
+
+ Default amount + ${defaultAmount} REEF +
+
+ Max amount + ${maxAmount} REEF +
+
+ WS upstream + ${wsEndpoint} +
+
+ EVM upstream + ${evmRpcUrl} +
+
+ +
+ + + + +
+ + +
+
+ +
+
Ready.
+
+ + +
+
+ + + +`; +} + async function parseRequestBody(request) { const chunks = []; for await (const chunk of request) { @@ -248,25 +656,18 @@ function enqueue(task) { const server = http.createServer(async (request, response) => { try { + if (request.method === "GET" && request.url === "/favicon.ico") { + response.writeHead(204); + response.end(); + return; + } + if (request.method === "GET" && request.url === "/") { - jsonResponse(response, 200, { - service: "reef-dev-cluster-faucet", - status: "ok", - endpoints: { - health: { - method: "GET", - path: "/health", - }, - drip: { - method: "POST", - path: "/drip", - body: { - to: "0x0000000000000000000000000000000000000000", - amount: config.defaultAmount, - }, - }, - }, - }); + if (wantsHtml(request)) { + htmlResponse(response, 200, buildFrontendHtml()); + } else { + jsonResponse(response, 200, buildInfoPayload()); + } return; } diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 22d4039e6..5316eb7b2 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -487,12 +487,14 @@ start_logged /tmp/rpc-node.log \ start_logged /tmp/faucet.log \ env \ - PORT="$FAUCET_PORT" \ - WS_ENDPOINT="ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ - FAUCET_SEED="$FAUCET_SEED" \ - DEFAULT_AMOUNT="$DEFAULT_AMOUNT" \ - MAX_AMOUNT="$MAX_AMOUNT" \ - npm --prefix "$FAUCET_DIR" start + SERVICE_NAME="reef-dev-cluster-faucet" \ + PORT="$FAUCET_PORT" \ + WS_ENDPOINT="ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ + EVM_RPC_URL="http://127.0.0.1:${ETH_RPC_PORT}" \ + FAUCET_SEED="$FAUCET_SEED" \ + DEFAULT_AMOUNT="$DEFAULT_AMOUNT" \ + MAX_AMOUNT="$MAX_AMOUNT" \ + npm --prefix "$FAUCET_DIR" start tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/faucet.log & TAIL_PID=$! diff --git a/blueprints/reef-faucet/faucet/package.json b/blueprints/reef-faucet/faucet/package.json new file mode 100644 index 000000000..b2da90457 --- /dev/null +++ b/blueprints/reef-faucet/faucet/package.json @@ -0,0 +1,13 @@ +{ + "name": "reef-faucet", + "version": "1.0.0", + "private": true, + "description": "HTTP faucet for Reef deployments", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "@polkadot/api": "^16.4.8" + } +} diff --git a/blueprints/reef-faucet/faucet/server.js b/blueprints/reef-faucet/faucet/server.js new file mode 100644 index 000000000..22e5bb386 --- /dev/null +++ b/blueprints/reef-faucet/faucet/server.js @@ -0,0 +1,696 @@ +#!/usr/bin/env node + +const http = require("node:http"); +const { ApiPromise, Keyring, WsProvider } = require("@polkadot/api"); + +const config = { + wsEndpoint: process.env.WS_ENDPOINT || "ws://127.0.0.1:9944", + evmRpcUrl: process.env.EVM_RPC_URL || "", + faucetSeed: process.env.FAUCET_SEED || "", + port: Number.parseInt(process.env.PORT || "8080", 10), + defaultAmount: String(process.env.DEFAULT_AMOUNT || "1"), + maxAmount: String(process.env.MAX_AMOUNT || "10"), + serviceName: process.env.SERVICE_NAME || "reef-faucet", +}; + +if (!config.faucetSeed) { + throw new Error("FAUCET_SEED is required"); +} + +let contextPromise = null; +let requestQueue = Promise.resolve(); + +function normalizeEvmAddress(value) { + const normalized = String(value || "").trim(); + if (!/^0x[0-9a-fA-F]{40}$/.test(normalized)) { + throw new Error(`Invalid EVM address: ${value}`); + } + return normalized; +} + +function parseUnits(value, decimals) { + const input = String(value).trim(); + if (!/^\d+(\.\d+)?$/.test(input)) { + throw new Error(`Invalid decimal amount: ${value}`); + } + + const [whole, fraction = ""] = input.split("."); + if (fraction.length > decimals) { + throw new Error(`Too many decimal places in ${value}`); + } + + const wholeUnits = BigInt(whole) * 10n ** BigInt(decimals); + const fractionUnits = fraction ? BigInt(fraction.padEnd(decimals, "0")) : 0n; + return wholeUnits + fractionUnits; +} + +function formatUnits(value, decimals) { + const negative = value < 0n; + const abs = negative ? -value : value; + const base = 10n ** BigInt(decimals); + const whole = abs / base; + const fraction = abs % base; + + if (fraction === 0n) { + return `${negative ? "-" : ""}${whole.toString()}`; + } + + return `${negative ? "-" : ""}${whole.toString()}.${fraction + .toString() + .padStart(decimals, "0") + .replace(/0+$/, "")}`; +} + +function fallbackAccountFromEvm(evmAddress) { + return `0x${evmAddress.slice(2).toLowerCase()}${"ee".repeat(12)}`; +} + +function decodeDispatchError(api, dispatchError) { + if (dispatchError && dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + return `${decoded.section}.${decoded.name}: ${decoded.docs.join(" ")}`; + } + + return dispatchError && dispatchError.toString ? dispatchError.toString() : String(dispatchError); +} + +async function signAndWait(api, tx, signer) { + return new Promise(async (resolve, reject) => { + let unsubscribe = null; + + try { + unsubscribe = await tx.signAndSend(signer, ({ dispatchError, events, status, txHash }) => { + if (dispatchError) { + if (unsubscribe) { + unsubscribe(); + } + reject(new Error(decodeDispatchError(api, dispatchError))); + return; + } + + if (status.isFinalized) { + if (unsubscribe) { + unsubscribe(); + } + resolve({ + txHash: txHash.toHex(), + finalizedBlock: status.asFinalized.toHex(), + eventCount: events.length, + }); + } + }); + } catch (error) { + if (unsubscribe) { + unsubscribe(); + } + reject(error); + } + }); +} + +async function maybeClaimDefaultAccount(api, signer) { + const storage = api.query && api.query.evmAccounts && api.query.evmAccounts.evmAddresses; + const extrinsic = api.tx && api.tx.evmAccounts && api.tx.evmAccounts.claimDefaultAccount; + + if (!storage || !extrinsic) { + return null; + } + + const current = await storage(signer.address); + if (!current.isEmpty) { + return current.toString(); + } + + await signAndWait(api, extrinsic(), signer); + return (await storage(signer.address)).toString(); +} + +function buildReviveTransfer(api, signerAddress, targetEvm, amountUnits) { + const reviveTransfer = api.tx && api.tx.revive && api.tx.revive.transfer; + if (!reviveTransfer) { + return null; + } + + const metaArgs = reviveTransfer.meta.toJSON().args || []; + const names = metaArgs.map((arg) => String((arg && arg.name) || "").toLowerCase()); + const types = metaArgs.map((arg) => JSON.stringify((arg && arg.type) || "").toLowerCase()); + const haystack = `${names.join(",")}|${types.join(",")}`; + + if (metaArgs.length === 2 && haystack.includes("h160")) { + return { + strategy: "revive.transfer(H160, Balance)", + tx: reviveTransfer(targetEvm, amountUnits), + }; + } + + if (metaArgs.length === 3 && haystack.includes("accountid") && haystack.includes("h160")) { + return { + strategy: "revive.transfer(AccountId, H160, Balance)", + tx: reviveTransfer(signerAddress, targetEvm, amountUnits), + }; + } + + return { + strategy: "revive.transfer(fallback call signature)", + tx: reviveTransfer(targetEvm, amountUnits), + }; +} + +async function getContext() { + if (!contextPromise) { + contextPromise = (async () => { + const provider = new WsProvider(config.wsEndpoint); + const api = await ApiPromise.create({ provider }); + const keyring = new Keyring({ type: "sr25519" }); + const sender = keyring.addFromUri(config.faucetSeed); + const nativeDecimals = api.registry.chainDecimals[0] || 12; + const tokenSymbol = api.registry.chainTokens[0] || "REEF"; + + return { api, nativeDecimals, provider, sender, tokenSymbol }; + })(); + } + + return contextPromise; +} + +function jsonResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "application/json" }); + response.end(JSON.stringify(body)); +} + +function htmlResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" }); + response.end(body); +} + +function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function wantsHtml(request) { + const accept = String(request.headers.accept || ""); + return accept.includes("text/html"); +} + +function buildInfoPayload() { + return { + service: config.serviceName, + status: "ok", + defaults: { + defaultAmount: config.defaultAmount, + maxAmount: config.maxAmount, + }, + upstream: { + wsEndpoint: config.wsEndpoint, + evmRpcUrl: config.evmRpcUrl || null, + }, + endpoints: { + health: { + method: "GET", + path: "/health", + }, + drip: { + method: "POST", + path: "/drip", + body: { + to: "0x0000000000000000000000000000000000000000", + amount: config.defaultAmount, + }, + }, + }, + }; +} + +function buildFrontendHtml() { + const bootPayload = JSON.stringify(buildInfoPayload()); + const title = escapeHtml(config.serviceName); + const defaultAmount = escapeHtml(config.defaultAmount); + const maxAmount = escapeHtml(config.maxAmount); + const wsEndpoint = escapeHtml(config.wsEndpoint); + const evmRpcUrl = escapeHtml(config.evmRpcUrl || "Not configured"); + + return ` + + + + + ${title} + + + +
+
+
+

Reef Faucet

+

${title}

+

Send a small amount of test REEF to any EVM address without reaching for curl first.

+
+ +
+
+ Default amount + ${defaultAmount} REEF +
+
+ Max amount + ${maxAmount} REEF +
+
+ WS upstream + ${wsEndpoint} +
+
+ EVM upstream + ${evmRpcUrl} +
+
+ +
+ + + + +
+ + +
+
+ +
+
Ready.
+
+ + +
+
+ + + +`; +} + +async function parseRequestBody(request) { + const chunks = []; + for await (const chunk of request) { + chunks.push(chunk); + } + + if (chunks.length === 0) { + return {}; + } + + return JSON.parse(Buffer.concat(chunks).toString("utf8")); +} + +async function executeDrip(targetEvm, amountText) { + const { api, nativeDecimals, sender, tokenSymbol } = await getContext(); + const amountUnits = parseUnits(amountText, nativeDecimals); + const maxUnits = parseUnits(config.maxAmount, nativeDecimals); + + if (amountUnits <= 0n) { + throw new Error("Amount must be greater than zero"); + } + + if (amountUnits > maxUnits) { + throw new Error(`Amount exceeds MAX_AMOUNT (${config.maxAmount})`); + } + + await maybeClaimDefaultAccount(api, sender); + + const senderBefore = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + let strategy = ""; + let result = null; + + try { + const revive = buildReviveTransfer(api, sender.address, targetEvm, amountUnits); + if (!revive) { + throw new Error("revive.transfer unavailable"); + } + strategy = revive.strategy; + result = await signAndWait(api, revive.tx, sender); + } catch (error) { + strategy = "balances.transferAllowDeath(AccountId32 fallback)"; + result = await signAndWait( + api, + api.tx.balances.transferAllowDeath(fallbackAccountFromEvm(targetEvm), amountUnits), + sender + ); + } + + const senderAfter = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + return { + amount: amountText, + strategy, + tokenSymbol, + txHash: result.txHash, + finalizedBlock: result.finalizedBlock, + eventCount: result.eventCount, + sender: sender.address, + senderNativeBefore: formatUnits(senderBefore, nativeDecimals), + senderNativeAfter: formatUnits(senderAfter, nativeDecimals), + }; +} + +function enqueue(task) { + const current = requestQueue.then(task, task); + requestQueue = current.catch(() => undefined); + return current; +} + +const server = http.createServer(async (request, response) => { + try { + if (request.method === "GET" && request.url === "/favicon.ico") { + response.writeHead(204); + response.end(); + return; + } + + if (request.method === "GET" && request.url === "/") { + if (wantsHtml(request)) { + htmlResponse(response, 200, buildFrontendHtml()); + } else { + jsonResponse(response, 200, buildInfoPayload()); + } + return; + } + + if (request.method === "GET" && request.url === "/health") { + jsonResponse(response, 200, { status: "ok" }); + return; + } + + if (request.method === "POST" && request.url === "/drip") { + const payload = await parseRequestBody(request); + const targetEvm = normalizeEvmAddress(payload.to); + const amount = payload.amount ? String(payload.amount) : config.defaultAmount; + const result = await enqueue(() => executeDrip(targetEvm, amount)); + jsonResponse(response, 200, result); + return; + } + + jsonResponse(response, 404, { error: "Not found" }); + } catch (error) { + jsonResponse(response, 400, { error: error.message }); + } +}); + +server.listen(config.port, "0.0.0.0", () => { + console.log(`${config.serviceName} listening on ${config.port}`); +}); From dfcac1a6984760495a01ae8666a70e129ee0a6ab Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:46:38 +0530 Subject: [PATCH 81/90] fix: pin reef faucet frontend assets --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- blueprints/reef-faucet/docker-compose.yml | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 15efd83d0..eec5322b0 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -45,13 +45,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/86bdfc6d2646e38c84fcbff63b8af35454404ee7/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=86bdfc6d2646e38c84fcbff63b8af35454404ee7 /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=182735be1afc163b0e99f28871ec5b6cb3c03cc3 /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml index ae156e779..432ec9ddf 100644 --- a/blueprints/reef-faucet/docker-compose.yml +++ b/blueprints/reef-faucet/docker-compose.yml @@ -10,6 +10,7 @@ services: - "${PORT}" environment: + SERVICE_NAME: reef-faucet WS_ENDPOINT: ${WS_ENDPOINT} EVM_RPC_URL: ${EVM_RPC_URL} FAUCET_SEED: ${FAUCET_SEED} @@ -36,9 +37,9 @@ services: echo '📥 Downloading faucet sources...' && mkdir -p /workspace/faucet && wget -O /workspace/faucet/package.json \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/faucet/package.json && + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-faucet/faucet/package.json && wget -O /workspace/faucet/server.js \ - https://raw.githubusercontent.com/anukulpandey/dokploy-reef-chain-scripts/52c3e30d06bba10936aea2db9740eafd76b3ac40/faucet/server.js && + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-faucet/faucet/server.js && echo '📦 Installing faucet npm dependencies...' && cd /workspace/faucet && From 652d7cb5ee3327333bfa3c000c4348bba32e8dab Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:54:31 +0530 Subject: [PATCH 82/90] fix: make reef dev cluster eth rpc dokploy-safe --- blueprints/reef-dev-cluster/docker-compose.yml | 10 +++++++--- blueprints/reef-dev-cluster/template.toml | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index eec5322b0..a24329bb5 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -25,7 +25,6 @@ services: - "30336" - "8001" - "9944" - - "8545" - "8080" entrypoint: /bin/sh @@ -59,15 +58,20 @@ services: working_dir: /workspace depends_on: - reef-dev-cluster - network_mode: "service:reef-dev-cluster" + expose: + - "8545" environment: - NODE_RPC_URL: ws://127.0.0.1:9944 + NODE_RPC_URL: ws://reef-dev-cluster:9944 PORT: "8545" entrypoint: /bin/sh command: - -c - | set -e + until bash -lc 'exec 3<>/dev/tcp/reef-dev-cluster/9944'; do + echo 'eth-rpc is waiting for the ws rpc socket...' + sleep 2 + done until eth-rpc --node-rpc-url "$$NODE_RPC_URL" --rpc-port "$$PORT" --rpc-external --rpc-cors all; do echo 'eth-rpc is waiting for the ws rpc node...' sleep 2 diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index e2bbe4698..f210700c3 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -28,7 +28,7 @@ host = "ws.${domain}" path = "/" [[config.domains]] -serviceName = "reef-dev-cluster" +serviceName = "reef-dev-cluster-eth-rpc" port = 8545 host = "eth.${domain}" path = "/" From c095e5d88f1f2e391173388acda5724c6e29911b Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 20:58:35 +0530 Subject: [PATCH 83/90] fix: gate reef dev cluster eth rpc on health --- .../reef-dev-cluster/docker-compose.yml | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index a24329bb5..b7bf4feeb 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -27,6 +27,15 @@ services: - "9944" - "8080" + healthcheck: + test: + - CMD-SHELL + - bash -lc 'exec 3<>/dev/tcp/127.0.0.1/9944' + interval: 10s + timeout: 5s + retries: 30 + start_period: 300s + entrypoint: /bin/sh command: - -c @@ -57,25 +66,25 @@ services: restart: unless-stopped working_dir: /workspace depends_on: - - reef-dev-cluster + reef-dev-cluster: + condition: service_healthy expose: - "8545" environment: NODE_RPC_URL: ws://reef-dev-cluster:9944 PORT: "8545" + healthcheck: + test: + - CMD-SHELL + - bash -lc 'exec 3<>/dev/tcp/127.0.0.1/8545' + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s entrypoint: /bin/sh command: - -c - - | - set -e - until bash -lc 'exec 3<>/dev/tcp/reef-dev-cluster/9944'; do - echo 'eth-rpc is waiting for the ws rpc socket...' - sleep 2 - done - until eth-rpc --node-rpc-url "$$NODE_RPC_URL" --rpc-port "$$PORT" --rpc-external --rpc-cors all; do - echo 'eth-rpc is waiting for the ws rpc node...' - sleep 2 - done + - exec eth-rpc --node-rpc-url "$$NODE_RPC_URL" --rpc-port "$$PORT" --rpc-external --rpc-cors all volumes: reef-dev-cluster-state: From 74b5720f059404408d1b9e6ed1e215e2ab09817e Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 21:26:00 +0530 Subject: [PATCH 84/90] fix: restore reef dev cluster ws rpc --- blueprints/reef-dev-cluster/docker-compose.yml | 5 +++-- blueprints/reef-dev-cluster/run-cluster.sh | 16 +++++++++++++--- blueprints/reef-dev-cluster/template.toml | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index b7bf4feeb..0231ea29b 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -25,6 +25,7 @@ services: - "30336" - "8001" - "9944" + - "9945" - "8080" healthcheck: @@ -44,7 +45,7 @@ services: echo '📦 Installing required tools...' apt-get update - apt-get install -y wget curl python3 ca-certificates gnupg + apt-get install -y wget curl python3 ca-certificates gnupg socat mkdir -p /etc/apt/keyrings /workspace /workspace/state /workspace/download curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg @@ -71,7 +72,7 @@ services: expose: - "8545" environment: - NODE_RPC_URL: ws://reef-dev-cluster:9944 + NODE_RPC_URL: ws://reef-dev-cluster:9945 PORT: "8545" healthcheck: test: diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 5316eb7b2..99737249c 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -19,6 +19,7 @@ VALIDATOR2_P2P_PORT="${VALIDATOR2_P2P_PORT:-30334}" VALIDATOR3_P2P_PORT="${VALIDATOR3_P2P_PORT:-30336}" RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" +RPC_NODE_PUBLIC_WS_PORT="${RPC_NODE_PUBLIC_WS_PORT:-9945}" ETH_RPC_PORT="${ETH_RPC_PORT:-8545}" FAUCET_PORT="${FAUCET_PORT:-8080}" BOOTNODE_PROMETHEUS_PORT="${BOOTNODE_PROMETHEUS_PORT:-9615}" @@ -192,7 +193,7 @@ write_cluster_info() { "$V3_ADDR" "$V3_SOURCE" "$VALIDATOR3_P2P_PORT" \ "$FAUCET_ADDR" "$FAUCET_SOURCE" "$FAUCET_PORT" \ "$BOOTNODE_PEER_ID" "$BOOTNODE_P2P_PORT" \ - "$RPC_NODE_P2P_PORT" "$RPC_NODE_WS_PORT" "$ETH_RPC_PORT" \ + "$RPC_NODE_P2P_PORT" "$RPC_NODE_PUBLIC_WS_PORT" "$ETH_RPC_PORT" \ "$SPEC_HTTP_PORT" "$STATE_DIR" <<'PY' import json import sys @@ -480,11 +481,20 @@ start_logged /tmp/rpc-node.log \ --prometheus-port "$RPC_NODE_PROMETHEUS_PORT" \ --node-key-file "$RPC_NODE_KEY_FILE" \ --bootnodes "$BOOTNODE_MULTIADDR" \ - --experimental-rpc-endpoint "listen-addr=0.0.0.0:${RPC_NODE_WS_PORT},cors=all,methods=unsafe,max-connections=10000" \ + --rpc-external \ + --rpc-port "$RPC_NODE_WS_PORT" \ + --rpc-cors all \ + --rpc-methods Unsafe \ + --rpc-max-connections 10000 \ --pruning archive \ --name rpc-node \ --no-telemetry +start_logged /tmp/ws-proxy.log \ + socat \ + "TCP-LISTEN:${RPC_NODE_PUBLIC_WS_PORT},fork,reuseaddr,bind=0.0.0.0" \ + "TCP:127.0.0.1:${RPC_NODE_WS_PORT}" + start_logged /tmp/faucet.log \ env \ SERVICE_NAME="reef-dev-cluster-faucet" \ @@ -496,7 +506,7 @@ start_logged /tmp/faucet.log \ MAX_AMOUNT="$MAX_AMOUNT" \ npm --prefix "$FAUCET_DIR" start -tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/faucet.log & +tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/ws-proxy.log /tmp/faucet.log & TAIL_PID=$! wait -n "${PIDS[@]}" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index f210700c3..af2bef139 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -23,7 +23,7 @@ path = "/" [[config.domains]] serviceName = "reef-dev-cluster" -port = 9944 +port = 9945 host = "ws.${domain}" path = "/" From 5e8b05385d2e2b460a5ed8bfe502004b18f09b39 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 21:26:18 +0530 Subject: [PATCH 85/90] fix: pin reef dev cluster ws assets --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 0231ea29b..8c9171596 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -54,13 +54,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/74b5720f059404408d1b9e6ed1e215e2ab09817e/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=182735be1afc163b0e99f28871ec5b6cb3c03cc3 /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=74b5720f059404408d1b9e6ed1e215e2ab09817e /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest From 1d644e42fb375f1d6034f493b6b3d6891140dede Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Fri, 17 Apr 2026 21:30:53 +0530 Subject: [PATCH 86/90] fix: proxy reef dev cluster ws host headers --- .../reef-dev-cluster/docker-compose.yml | 2 +- blueprints/reef-dev-cluster/run-cluster.sh | 41 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 8c9171596..6f9f3d5d6 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -45,7 +45,7 @@ services: echo '📦 Installing required tools...' apt-get update - apt-get install -y wget curl python3 ca-certificates gnupg socat + apt-get install -y wget curl python3 ca-certificates gnupg nginx mkdir -p /etc/apt/keyrings /workspace /workspace/state /workspace/download curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 99737249c..19cedca4e 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -186,6 +186,39 @@ start_logged() { PIDS+=("$!") } +write_ws_proxy_config() { + cat > /tmp/rpc-nginx.conf < Date: Fri, 17 Apr 2026 21:31:20 +0530 Subject: [PATCH 87/90] fix: pin reef dev cluster host proxy assets --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 6f9f3d5d6..068fe2903 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -54,13 +54,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/74b5720f059404408d1b9e6ed1e215e2ab09817e/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/1d644e42fb375f1d6034f493b6b3d6891140dede/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=74b5720f059404408d1b9e6ed1e215e2ab09817e /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=1d644e42fb375f1d6034f493b6b3d6891140dede /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest From d8a82420dad013a3eb7e5d8afc0b9f2700a0045a Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 23 Apr 2026 11:26:06 +0530 Subject: [PATCH 88/90] fix: raise reef faucet drip amount --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- blueprints/reef-dev-cluster/faucet/server.js | 6 +++--- blueprints/reef-dev-cluster/run-cluster.sh | 4 ++-- blueprints/reef-dev-cluster/template.toml | 8 ++++---- blueprints/reef-faucet/faucet/server.js | 6 +++--- blueprints/reef-faucet/template.toml | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index 068fe2903..a2d7aeead 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -11,8 +11,8 @@ services: v2sec: "${v2sec:-}" v3sec: "${v3sec:-}" faucetsec: "${faucetsec:-}" - DEFAULT_AMOUNT: "${DEFAULT_AMOUNT:-1}" - MAX_AMOUNT: "${MAX_AMOUNT:-10}" + DEFAULT_AMOUNT: "${DEFAULT_AMOUNT:-2000}" + MAX_AMOUNT: "${MAX_AMOUNT:-2000}" volumes: - reef-dev-cluster-state:/workspace/state diff --git a/blueprints/reef-dev-cluster/faucet/server.js b/blueprints/reef-dev-cluster/faucet/server.js index 957bae12c..15d1ce32b 100644 --- a/blueprints/reef-dev-cluster/faucet/server.js +++ b/blueprints/reef-dev-cluster/faucet/server.js @@ -8,8 +8,8 @@ const config = { evmRpcUrl: process.env.EVM_RPC_URL || "", faucetSeed: process.env.FAUCET_SEED || "", port: Number.parseInt(process.env.PORT || "8080", 10), - defaultAmount: String(process.env.DEFAULT_AMOUNT || "1"), - maxAmount: String(process.env.MAX_AMOUNT || "10"), + defaultAmount: String(process.env.DEFAULT_AMOUNT || "2000"), + maxAmount: String(process.env.MAX_AMOUNT || "2000"), serviceName: process.env.SERVICE_NAME || "reef-faucet", }; @@ -444,7 +444,7 @@ function buildFrontendHtml() {

Reef Faucet

${title}

-

Send a small amount of test REEF to any EVM address without reaching for curl first.

+

Send test REEF to any EVM address without reaching for curl first.

diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh index 19cedca4e..4dca6dc68 100755 --- a/blueprints/reef-dev-cluster/run-cluster.sh +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -27,8 +27,8 @@ VALIDATOR1_PROMETHEUS_PORT="${VALIDATOR1_PROMETHEUS_PORT:-9616}" VALIDATOR2_PROMETHEUS_PORT="${VALIDATOR2_PROMETHEUS_PORT:-9617}" VALIDATOR3_PROMETHEUS_PORT="${VALIDATOR3_PROMETHEUS_PORT:-9618}" RPC_NODE_PROMETHEUS_PORT="${RPC_NODE_PROMETHEUS_PORT:-9619}" -DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-1}" -MAX_AMOUNT="${MAX_AMOUNT:-10}" +DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-2000}" +MAX_AMOUNT="${MAX_AMOUNT:-2000}" TEMPLATE_ASSET_REF="${TEMPLATE_ASSET_REF:-reef-chain}" TEMPLATE_ASSET_BASE_URL="${TEMPLATE_ASSET_BASE_URL:-https://raw.githubusercontent.com/anukulpandey/dokploy-templates/${TEMPLATE_ASSET_REF}/blueprints/reef-dev-cluster}" FAUCET_DIR="${FAUCET_DIR:-/workspace/faucet}" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml index af2bef139..05345c2bd 100644 --- a/blueprints/reef-dev-cluster/template.toml +++ b/blueprints/reef-dev-cluster/template.toml @@ -6,8 +6,8 @@ v3sec = "" # Optional dedicated faucet seed. Leave blank to reuse validator 1. faucetsec = "" -DEFAULT_AMOUNT = "1" -MAX_AMOUNT = "10" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" [[config.domains]] serviceName = "reef-dev-cluster" @@ -47,5 +47,5 @@ v3sec = "" # Optional dedicated faucet seed. Leave blank to reuse validator 1. faucetsec = "" -DEFAULT_AMOUNT = "1" -MAX_AMOUNT = "10" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" diff --git a/blueprints/reef-faucet/faucet/server.js b/blueprints/reef-faucet/faucet/server.js index 22e5bb386..379ffc0da 100644 --- a/blueprints/reef-faucet/faucet/server.js +++ b/blueprints/reef-faucet/faucet/server.js @@ -8,8 +8,8 @@ const config = { evmRpcUrl: process.env.EVM_RPC_URL || "", faucetSeed: process.env.FAUCET_SEED || "", port: Number.parseInt(process.env.PORT || "8080", 10), - defaultAmount: String(process.env.DEFAULT_AMOUNT || "1"), - maxAmount: String(process.env.MAX_AMOUNT || "10"), + defaultAmount: String(process.env.DEFAULT_AMOUNT || "2000"), + maxAmount: String(process.env.MAX_AMOUNT || "2000"), serviceName: process.env.SERVICE_NAME || "reef-faucet", }; @@ -444,7 +444,7 @@ function buildFrontendHtml() {

Reef Faucet

${title}

-

Send a small amount of test REEF to any EVM address without reaching for curl first.

+

Send test REEF to any EVM address without reaching for curl first.

diff --git a/blueprints/reef-faucet/template.toml b/blueprints/reef-faucet/template.toml index 073ad4c95..1b27dbee8 100644 --- a/blueprints/reef-faucet/template.toml +++ b/blueprints/reef-faucet/template.toml @@ -3,8 +3,8 @@ PORT = "8080" WS_ENDPOINT = "ws://ws.reef.host" EVM_RPC_URL = "http://eth.reef.host" FAUCET_SEED = "" -DEFAULT_AMOUNT = "1" -MAX_AMOUNT = "10" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" [[config.domains]] serviceName = "reef-faucet" @@ -23,5 +23,5 @@ PORT = "8080" WS_ENDPOINT = "ws://ws.reef.host" EVM_RPC_URL = "http://eth.reef.host" FAUCET_SEED = "" -DEFAULT_AMOUNT = "1" -MAX_AMOUNT = "10" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" From 03b11015e58f8be7f0f25dcd31936420f3d46f58 Mon Sep 17 00:00:00 2001 From: Anukul Pandey Date: Thu, 23 Apr 2026 11:26:30 +0530 Subject: [PATCH 89/90] fix: pin reef faucet amount assets --- blueprints/reef-dev-cluster/docker-compose.yml | 4 ++-- blueprints/reef-faucet/docker-compose.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index a2d7aeead..a42c00be4 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -54,13 +54,13 @@ services: apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/1d644e42fb375f1d6034f493b6b3d6891140dede/blueprints/reef-dev-cluster/run-cluster.sh + wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=1d644e42fb375f1d6034f493b6b3d6891140dede /workspace/run-cluster.sh + exec env TEMPLATE_ASSET_REF=d8a82420dad013a3eb7e5d8afc0b9f2700a0045a /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest diff --git a/blueprints/reef-faucet/docker-compose.yml b/blueprints/reef-faucet/docker-compose.yml index 432ec9ddf..fb12738d9 100644 --- a/blueprints/reef-faucet/docker-compose.yml +++ b/blueprints/reef-faucet/docker-compose.yml @@ -37,9 +37,9 @@ services: echo '📥 Downloading faucet sources...' && mkdir -p /workspace/faucet && wget -O /workspace/faucet/package.json \ - https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-faucet/faucet/package.json && + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-faucet/faucet/package.json && wget -O /workspace/faucet/server.js \ - https://raw.githubusercontent.com/anukulpandey/dokploy-templates/182735be1afc163b0e99f28871ec5b6cb3c03cc3/blueprints/reef-faucet/faucet/server.js && + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-faucet/faucet/server.js && echo '📦 Installing faucet npm dependencies...' && cd /workspace/faucet && From 66c59e6d606431d074d6465f2037605b505dd951 Mon Sep 17 00:00:00 2001 From: Anukul Pandey <62092256+anukulpandey@users.noreply.github.com> Date: Thu, 28 May 2026 17:15:29 +0530 Subject: [PATCH 90/90] Update docker-compose.yml --- .../reef-dev-cluster/docker-compose.yml | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml index a42c00be4..49b726af8 100644 --- a/blueprints/reef-dev-cluster/docker-compose.yml +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: reef-dev-cluster: image: anukulpandey/reef-chain-node:latest @@ -38,42 +36,74 @@ services: start_period: 300s entrypoint: /bin/sh + command: - -c - | set -e + export DEBIAN_FRONTEND=noninteractive + echo '📦 Installing required tools...' + apt-get update - apt-get install -y wget curl python3 ca-certificates gnupg nginx - mkdir -p /etc/apt/keyrings /workspace /workspace/state /workspace/download - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg - echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' > /etc/apt/sources.list.d/nodesource.list + apt-get install -y \ + wget \ + curl \ + python3 \ + ca-certificates \ + gnupg \ + nginx + + mkdir -p \ + /etc/apt/keyrings \ + /workspace \ + /workspace/state \ + /workspace/download + + echo '🔑 Configuring NodeSource repository...' + + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg + + echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' \ + > /etc/apt/sources.list.d/nodesource.list + apt-get update + apt-get install -y nodejs echo '📥 Downloading cluster bootstrap...' - wget -O /workspace/run-cluster.sh https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-dev-cluster/run-cluster.sh + + wget -O /workspace/run-cluster.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-dev-cluster/run-cluster.sh echo '🔧 Making script executable...' + chmod +x /workspace/run-cluster.sh echo '🚀 Starting self-contained network...' - exec env TEMPLATE_ASSET_REF=d8a82420dad013a3eb7e5d8afc0b9f2700a0045a /workspace/run-cluster.sh + + exec env TEMPLATE_ASSET_REF=d8a82420dad013a3eb7e5d8afc0b9f2700a0045a \ + /workspace/run-cluster.sh reef-dev-cluster-eth-rpc: image: anukulpandey/reef-chain-eth-rpc:latest restart: unless-stopped working_dir: /workspace + depends_on: reef-dev-cluster: condition: service_healthy + expose: - "8545" + environment: NODE_RPC_URL: ws://reef-dev-cluster:9945 PORT: "8545" + healthcheck: test: - CMD-SHELL @@ -82,10 +112,17 @@ services: timeout: 5s retries: 12 start_period: 20s + entrypoint: /bin/sh + command: - -c - - exec eth-rpc --node-rpc-url "$$NODE_RPC_URL" --rpc-port "$$PORT" --rpc-external --rpc-cors all + - | + exec eth-rpc \ + --node-rpc-url "$$NODE_RPC_URL" \ + --rpc-port "$$PORT" \ + --rpc-external \ + --rpc-cors all volumes: reef-dev-cluster-state: