diff --git a/operator/node/Cargo.toml b/operator/node/Cargo.toml index 8c6df8ed..68861344 100644 --- a/operator/node/Cargo.toml +++ b/operator/node/Cargo.toml @@ -141,7 +141,6 @@ toml = { workspace = true } #### Needed to build static binaries #### pq-sys = { workspace = true, optional = true } - [build-dependencies] substrate-build-script-utils = { workspace = true, default-features = true } diff --git a/test/cli/handlers/launch/storagehub.ts b/test/cli/handlers/launch/storagehub.ts index fc162dde..51eca67e 100644 --- a/test/cli/handlers/launch/storagehub.ts +++ b/test/cli/handlers/launch/storagehub.ts @@ -1,6 +1,7 @@ import { logger, printHeader } from "utils"; import type { DataHavenOptions } from "../../../launcher/datahaven"; import { + launchBackend, launchBspNode, launchFishermanNode, launchIndexerNode, @@ -99,5 +100,9 @@ async function launchStorageHubDocker( logger.info("📝 Registering providers..."); await registerProviders({ launchedNetwork }); + // Launch Backend MSP + logger.info("📦 Launching StorageHub Backend..."); + await launchBackend(datahavenOptions, launchedNetwork); + logger.success("All StorageHub components launched and registered"); } diff --git a/test/launcher/storagehub-docker.ts b/test/launcher/storagehub-docker.ts index c1de22a9..a9f64c0e 100644 --- a/test/launcher/storagehub-docker.ts +++ b/test/launcher/storagehub-docker.ts @@ -1,11 +1,15 @@ import { $ } from "bun"; import { getPublicPort, killExistingContainers, logger, waitForContainerToStart } from "utils"; -import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; +import { DEFAULT_SUBSTRATE_WS_PORT, SUBSTRATE_FUNDED_ACCOUNTS } from "utils/constants"; import { waitFor } from "utils/waits"; import type { DataHavenOptions } from "./datahaven"; import { isNetworkReady } from "./datahaven"; import type { LaunchedNetwork } from "./types/launchedNetwork"; +/** + * Important ! This is for local deployment only. We are using mDNS discovery when startinn node with the `--discover-local` flag + */ + /** * PostgreSQL configuration for StorageHub Indexer and Fisherman */ @@ -109,48 +113,25 @@ export const getPostgresUrl = (networkId: string): string => { * Injects a BCSV ECDSA key into a StorageHub provider node's keystore. * * @param containerName - Name of the Docker container - * @param seed - The seed phrase for key generation - * @param derivation - Key derivation path (e.g., "//Charlie") + * @param secretKey - The secret key (or private key) we want to add to the node */ export const injectStorageHubKey = async ( containerName: string, - seed: string, - derivation: string + secretKey: string ): Promise => { - logger.info(`🔑 Injecting key ${derivation} into ${containerName}...`); - - const suri = `${seed}${derivation}`; + logger.info(`🔑 Injecting key into ${containerName}...`); // Use Bun's $ directly with docker exec (no sh -c wrapper needed) // This properly handles the spaces in the seed phrase try { - await $`docker exec ${containerName} datahaven-node key insert --base-path /data --chain dev --key-type bcsv --scheme ecdsa --suri ${suri}`.nothrow(); - logger.success(`Key ${derivation} injected successfully`); + await $`docker exec ${containerName} datahaven-node key insert --base-path /data --key-type bcsv --scheme ecdsa --suri ${secretKey}`; + logger.success("Key injected successfully"); } catch (error) { - logger.error(`Failed to inject key ${derivation}: ${error}`); + logger.error(`Failed to inject key : ${error}`); throw error; } }; -/** - * Gets the bootnode address from a running validator node. - * - * For local development with Docker, nodes on the same network can discover each other - * via mDNS (--discover-local flag), so explicit bootnodes are optional. - * - * To use explicit bootnodes, we'd need to extract the peer ID from the validator node, - * which requires querying the RPC endpoint. For simplicity in local dev, we skip this. - * - * @param containerName - Name of the validator container (e.g., datahaven-alice-cli-launch) - * @returns Multiaddress string for bootnode, or empty string to skip bootnodes - */ -export const getBootnodeAddress = async (containerName: string): Promise => { - // For local Docker development, nodes discover each other via mDNS - // No explicit bootnode needed with --discover-local flag - logger.debug(`Skipping explicit bootnode for ${containerName} - using mDNS discovery`); - return ""; -}; - /** * Launches a StorageHub MSP (Main Storage Provider) node. * @@ -166,10 +147,6 @@ export const launchMspNode = async ( const containerName = `storagehub-msp-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9945; // External port for MSP node - const aliceContainer = `datahaven-alice-${options.networkId}`; - - // Get bootnode address (empty for local dev with mDNS discovery) - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const command: string[] = [ "docker", @@ -182,10 +159,10 @@ export const launchMspNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", "msp-charlie", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -208,19 +185,13 @@ export const launchMspNode = async ( "1073741824" // 1 GiB ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); await waitForContainerToStart(containerName); // Inject key - const seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; - await injectStorageHubKey(containerName, seed, "//Charlie"); + await injectStorageHubKey(containerName, SUBSTRATE_FUNDED_ACCOUNTS.CHARLETH.privateKey); // Restart container to load key logger.info("🔄 Restarting MSP node to load key..."); @@ -263,10 +234,6 @@ export const launchBspNode = async ( const containerName = `storagehub-bsp-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9946; // External port for BSP node - const aliceContainer = `datahaven-alice-${options.networkId}`; - - // Get bootnode address (empty for local dev with mDNS discovery) - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const command: string[] = [ "docker", @@ -279,10 +246,10 @@ export const launchBspNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", - "bsp-dave", + "bsp-dorothy", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -303,19 +270,13 @@ export const launchBspNode = async ( "1073741824" // 1 GiB ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); await waitForContainerToStart(containerName); - // Inject key (using Dave instead of Eve for BSP) - const seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; - await injectStorageHubKey(containerName, seed, "//Dave"); + // Inject key + await injectStorageHubKey(containerName, SUBSTRATE_FUNDED_ACCOUNTS.DOROTHY.privateKey); // Restart container to load key logger.info("🔄 Restarting BSP node to load key..."); @@ -358,10 +319,7 @@ export const launchIndexerNode = async ( const containerName = `storagehub-indexer-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9947; // External port for Indexer node - const aliceContainer = `datahaven-alice-${options.networkId}`; - // Get bootnode address (empty for local dev with mDNS discovery) and PostgreSQL URL - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const postgresUrl = getPostgresUrl(options.networkId); const command: string[] = [ @@ -375,10 +333,10 @@ export const launchIndexerNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", "indexer", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -395,11 +353,6 @@ export const launchIndexerNode = async ( postgresUrl ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); @@ -441,10 +394,7 @@ export const launchFishermanNode = async ( const containerName = `storagehub-fisherman-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9948; // External port for Fisherman node - const aliceContainer = `datahaven-alice-${options.networkId}`; - // Get bootnode address (empty for local dev with mDNS discovery) and PostgreSQL URL - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const postgresUrl = getPostgresUrl(options.networkId); const command: string[] = [ @@ -459,7 +409,7 @@ export const launchFishermanNode = async ( `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, "--chain", - "dev", + "local", "--name", "fisherman", "--rpc-port", @@ -476,11 +426,6 @@ export const launchFishermanNode = async ( postgresUrl ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); @@ -507,6 +452,63 @@ export const launchFishermanNode = async ( logger.success(`Fisherman node started on port ${wsPort}`); }; +/** + * Launches a StorageHub Backend container. + * + * @param options - Configuration options for launching the network + * @param launchedNetwork - The launched network instance to track the node + */ +export const launchBackend = async ( + options: DataHavenOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + logger.info("🚀 Launching StorageHub Backend..."); + + const backendImage = "moonsonglabs/storage-hub-msp-backend:latest"; + const containerName = `storagehub-backend-${options.networkId}`; + const dockerNetworkName = `datahaven-${options.networkId}`; + const containerNameMSP = `storagehub-msp-${options.networkId}`; + const postgresUrl = getPostgresUrl(options.networkId); + const apiPort = 8080; + + const command: string[] = [ + "docker", + "run", + "-d", + "--name", + containerName, + "--network", + dockerNetworkName, + "-p", + `${apiPort}:8080`, + "-e", + "RUST_LOG=info", + backendImage, + "--chain", + "local", + "--log-format", + "text", + "--database-url", + postgresUrl, + "--rpc-url", + `ws://${containerNameMSP}:${DEFAULT_SUBSTRATE_WS_PORT}`, + "--msp-callback-url", + `http://${containerName}:8080`, + "--msp-trusted-file-transfer-server-url", + `http://${containerNameMSP}:7070` + ]; + + logger.debug(`Executing: ${command.join(" ")}`); + await $`sh -c "${command.join(" ")}"`.nothrow(); + + await waitForContainerToStart(containerName); + + // Register in launched network + launchedNetwork.addContainer(containerName, { http: apiPort }, { http: apiPort }); + + logger.success(`StorageHub Backend container started on port ${apiPort}`); +}; + /** * Stops and removes all StorageHub containers. * diff --git a/test/scripts/fund-providers.ts b/test/scripts/fund-providers.ts index 1487fd2e..098226ea 100644 --- a/test/scripts/fund-providers.ts +++ b/test/scripts/fund-providers.ts @@ -7,32 +7,6 @@ export interface FundProvidersOptions { launchedNetwork: LaunchedNetwork; } -/** - * Provider account information for MSP and BSP nodes. - * - * DataHaven uses AccountId20 (Ethereum-style 20-byte addresses). - * In dev chains, CHARLETH and DOROTHY are pre-funded development accounts - * that correspond to //Charlie and //Dave derivations. - * - * For StorageHub providers, we use: - * - CHARLETH (//Charlie equivalent) for MSP - * - DOROTHY (//Dave equivalent) for BSP (as //Eve might not have pre-funded AccountId20) - */ -const PROVIDER_ACCOUNTS = { - // MSP account (Charleth = Charlie in AccountId20 format) - msp: { - name: "Charleth", - address: SUBSTRATE_FUNDED_ACCOUNTS.CHARLETH.publicKey, // 20-byte address - derivation: "//Charlie" - }, - // BSP account (Dorothy = Dave in AccountId20 format, using instead of Eve) - bsp: { - name: "Dorothy", - address: SUBSTRATE_FUNDED_ACCOUNTS.DOROTHY.publicKey, // 20-byte address - derivation: "//Dave" // Using Dave instead of Eve for BSP - } -} as const; - /** * Minimum balance required for provider operations. * This includes: @@ -61,30 +35,34 @@ export async function fundProviders(options: FundProvidersOptions): Promise