From 2186c323e949cd7b627f71408b823489165dfd91 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 10 Nov 2025 16:36:08 -0500 Subject: [PATCH 1/7] wizard draft --- .../console/layer-1/AvalancheGoDockerL1.tsx | 618 ++++++++++++------ .../toolbox/console/layer-1/node-config.ts | 192 ++++++ 2 files changed, 612 insertions(+), 198 deletions(-) create mode 100644 components/toolbox/console/layer-1/node-config.ts diff --git a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx index 4f3a63f29ea..8372e3b0869 100644 --- a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx +++ b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx @@ -7,18 +7,13 @@ import { Container } from "../../components/Container"; import { getBlockchainInfo, getSubnetInfo } from "../../coreViem/utils/glacier"; import InputSubnetId from "../../components/InputSubnetId"; import BlockchainDetailsDisplay from "../../components/BlockchainDetailsDisplay"; -import { Steps, Step } from "fumadocs-ui/components/steps"; import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock'; import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; import { Button } from "../../components/Button"; -import { Success } from "../../components/Success"; -import { DockerInstallation } from "../../components/DockerInstallation"; -import { NodeBootstrapCheck } from "../../components/NodeBootstrapCheck"; -import { ReverseProxySetup } from "../../components/ReverseProxySetup"; -import { AddToWalletStep } from "../../components/AddToWalletStep"; -import { ConfigureNodeType } from "../../components/ConfigureNodeType"; -import { generateDockerCommand } from "./create/config"; +import { Steps, Step } from "fumadocs-ui/components/steps"; +import { SyntaxHighlightedJSON } from "../../components/genesis/SyntaxHighlightedJSON"; import { SUBNET_EVM_VM_ID } from "@/constants/console"; +import { generateChainConfig, generateDockerCommand, generateConfigFileCommand } from "./node-config"; export default function AvalanchegoDocker() { const [chainId, setChainId] = useState(""); @@ -27,38 +22,58 @@ export default function AvalanchegoDocker() { const [blockchainInfo, setBlockchainInfo] = useState(null); const [isLoading, setIsLoading] = useState(false); const [nodeType, setNodeType] = useState<"validator" | "public-rpc">("validator"); - const [rpcCommand, setRpcCommand] = useState(""); const [domain, setDomain] = useState(""); const [enableDebugTrace, setEnableDebugTrace] = useState(false); const [pruningEnabled, setPruningEnabled] = useState(true); const [subnetIdError, setSubnetIdError] = useState(null); - const [chainAddedToWallet, setChainAddedToWallet] = useState(null); - const [nodeIsReady, setNodeIsReady] = useState(false); const [selectedRPCBlockchainId, setSelectedRPCBlockchainId] = useState(""); const [minDelayTarget, setMinDelayTarget] = useState(250); + const [configJson, setConfigJson] = useState(""); + + // Advanced cache settings + const [trieCleanCache, setTrieCleanCache] = useState(512); + const [trieDirtyCache, setTrieDirtyCache] = useState(512); + const [snapshotCache, setSnapshotCache] = useState(256); + const [commitInterval, setCommitInterval] = useState(4096); + + // API settings + const [rpcGasCap, setRpcGasCap] = useState(50000000); + const [apiMaxBlocksPerRequest, setApiMaxBlocksPerRequest] = useState(0); + const [allowUnfinalizedQueries, setAllowUnfinalizedQueries] = useState(false); + + // Show advanced settings + const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); const { avalancheNetworkID } = useWalletStore(); const isRPC = nodeType === "public-rpc"; + // Generate Subnet-EVM chain configuration JSON when parameters change useEffect(() => { + if (!subnetId || !chainId || !blockchainInfo) { + setConfigJson(""); + return; + } + try { - const vmId = blockchainInfo?.vmId || SUBNET_EVM_VM_ID; - setRpcCommand(generateDockerCommand( - [subnetId], - isRPC, - avalancheNetworkID, - chainId, - vmId, + const config = generateChainConfig( + nodeType, enableDebugTrace, pruningEnabled, - false, // isPrimaryNetwork = false - isRPC ? null : minDelayTarget // Pass minDelayTarget only for validators - )); + minDelayTarget, + trieCleanCache, + trieDirtyCache, + snapshotCache, + commitInterval, + rpcGasCap, + apiMaxBlocksPerRequest, + allowUnfinalizedQueries + ); + setConfigJson(JSON.stringify(config, null, 2)); } catch (error) { - setRpcCommand((error as Error).message); + setConfigJson(`Error: ${(error as Error).message}`); } - }, [subnetId, isRPC, avalancheNetworkID, enableDebugTrace, chainId, pruningEnabled, blockchainInfo, minDelayTarget]); + }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, snapshotCache, commitInterval, rpcGasCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries]); useEffect(() => { if (nodeType === "validator") { @@ -140,57 +155,38 @@ export default function AvalanchegoDocker() { setSubnet(null); setBlockchainInfo(null); setNodeType("validator"); - setChainAddedToWallet(null); - setRpcCommand(""); setDomain(""); setEnableDebugTrace(false); setPruningEnabled(true); setSubnetIdError(null); - setNodeIsReady(false); setSelectedRPCBlockchainId(""); setMinDelayTarget(250); + setConfigJson(""); + setTrieCleanCache(512); + setTrieDirtyCache(512); + setSnapshotCache(256); + setCommitInterval(4096); + setRpcGasCap(50000000); + setApiMaxBlocksPerRequest(0); + setAllowUnfinalizedQueries(false); + setShowAdvancedSettings(false); }; // Check if this blockchain uses a custom VM const isCustomVM = blockchainInfo && blockchainInfo.vmId !== SUBNET_EVM_VM_ID; return ( - <> -

Set up Instance

-

Set up a linux server with any cloud provider, like AWS, GCP, Azure, or Digital Ocean. Low specs (e.g. 2 vCPUs, 4GB RAM, 20GB storage) are sufficient for basic tests. For more extensive test and production L1s use a larger instance with appropriate resources (e.g. 8 vCPUs, 16GB RAM, 1 TB storage).

- -

If you do not have access to a server, you can also run a node for educational purposes locally. Simply select the "RPC Node (Local)" option in the next step.

- -
- - - -

- If you do not want to use Docker, you can follow the instructions{" "} - - here - - . -

-
- - -

Select L1

-

Enter the Avalanche Subnet ID of the L1 you want to run a node for.

+

Select L1

+

+ Enter the Avalanche Subnet ID of the L1 you want to run a node for +

- {/* Show subnet details if available */} {subnet && subnet.blockchains && subnet.blockchains.length > 0 && ( -
+
{subnet.blockchains.map((blockchain: { blockchainId: string; blockchainName: string; createBlockTimestamp: number; createBlockNumber: string; vmId: string; subnetId: string; evmChainId: number }) => ( - - {/* Blockchain selection for multiple blockchains */} - {nodeType === "public-rpc" && subnet && subnet.blockchains && subnet.blockchains.length > 1 && ( -
+

Configure Node Settings

+ +
+
+
+ + +
+ + {nodeType === "public-rpc" && ( + <> +
+ +
+ +
+ +
+ + {subnet && subnet.blockchains && subnet.blockchains.length > 1 && ( +
@@ -251,9 +280,26 @@ export default function AvalanchegoDocker() {
)} - {/* Min delay target for validator nodes */} +
+ + setDomain(e.target.value)} + placeholder="example.com" + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" + /> +

+ If you plan to expose this RPC publicly, enter your domain name. +

+
+ + )} + {nodeType === "validator" && ( -
+
@@ -269,20 +315,307 @@ export default function AvalanchegoDocker() { className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" />

- The minimum delay between blocks (in milliseconds) that this node will attempt to use when creating blocks. Maximum: 2000ms. Default for L1: 250ms. + The minimum delay between blocks (in milliseconds). Maximum: 2000ms. Default: 250ms.

)} - + + {/* Advanced Settings */} +
+ + + {showAdvancedSettings && ( +
+
+

Cache Settings

+ +
+
+ + setTrieCleanCache(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
+ +
+ + setTrieDirtyCache(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
+ +
+ + setSnapshotCache(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
+
+
+ +
+

Performance Settings

+ +
+
+ + setCommitInterval(Math.max(1, parseInt(e.target.value) || 1))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

+ Interval to persist EVM and atomic tries +

+
+ +
+ + setRpcGasCap(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

+ Maximum gas limit for RPC calls +

+
+
+
+ + {isRPC && ( +
+

RPC Settings

+ +
+
+ + setApiMaxBlocksPerRequest(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

+ 0 = no limit. Limits blocks per getLogs request +

+
+ +
+ +

+ Allows queries for unfinalized blocks +

+
+
+
+ )} +
+ )} +
+
+ + {/* Configuration Preview */} +
+
+
+

Configuration Preview

+
+
+ {configJson && !configJson.startsWith("Error:") ? ( + + ) : ( +
+ {configJson.startsWith("Error:") ? configJson : "Configure your node to see the Subnet-EVM chain config"} +
+ )} +
+
+
+
-

Start AvalancheGo Node

-

Run the following Docker command to start your node:

+

Create Configuration File

+

+ Run this command on your server to create the Subnet-EVM chain configuration file: +

+ + { + try { + const config = JSON.parse(configJson); + return generateConfigFileCommand(chainId, config); + } catch { + return "# Error generating config file command"; + } + })()} + /> + +

+ This creates the configuration file at ~/.avalanchego/configs/chains/{chainId}/config.json +

+ + + +
+

Basic Settings:

+
    +
  • pruning-enabled: Enables state pruning to save disk space
  • +
  • log-level: Logging level (trace, debug, info, warn, error, crit)
  • +
  • min-delay-target: Minimum delay between blocks in milliseconds
  • +
  • warp-api-enabled: Enables the Warp API for cross-chain messaging (ICM)
  • +
+ +

Cache Settings:

+
    +
  • trie-clean-cache: Size of the trie clean cache in MB (default: 512)
  • +
  • trie-dirty-cache: Size of the trie dirty cache in MB (default: 512)
  • +
  • snapshot-cache: Size of the snapshot disk layer clean cache in MB (default: 256)
  • +
- +

Performance Settings:

+
    +
  • commit-interval: Interval at which to persist EVM and atomic tries (blocks, default: 4096)
  • +
  • rpc-gas-cap: Maximum gas limit for RPC calls (default: 50,000,000)
  • +
+ + {isRPC && ( + <> +

RPC-Specific Settings:

+
    +
  • api-max-blocks-per-request: Maximum blocks per getLogs request (0 = no limit)
  • +
  • allow-unfinalized-queries: Allows queries for unfinalized blocks
  • +
+ + )} + + {enableDebugTrace && ( + <> +

Debug Settings:

+
    +
  • eth-apis: Extended list of Ethereum APIs including debug and tracing capabilities
  • +
  • admin-api-enabled: Enables administrative operations API
  • +
+ + )} +
+
+ + {isCustomVM && ( + +

+ This blockchain uses a non-standard Virtual Machine ID. The Docker command includes VM aliases mapping. +

+

+ VM ID: {blockchainInfo.vmId}
+ Aliases to: {SUBNET_EVM_VM_ID} +

+
+ )} +
+
+ + +

Run Docker Command

+

+ Start the node using Docker: +

+ + { + try { + const config = JSON.parse(configJson); + const vmId = blockchainInfo?.vmId || SUBNET_EVM_VM_ID; + return generateDockerCommand( + subnetId, + chainId, + config, + nodeType, + avalancheNetworkID, + vmId + ); + } catch { + return "# Error generating Docker command"; + } + })()} + />

+ The container will read the config from ~/.avalanchego/configs/chains/{chainId}/config.json via the mounted volume. +

+ + + +

To run multiple nodes on the same machine, ensure each node has:

+
    +
  • Unique container name (change --name parameter)
  • +
  • Different ports (modify port mappings)
  • +
  • Separate data directories (change ~/.avalanchego path)
  • +
+
+ + +

Monitor your node with:

+ +
+ + +

For advanced node configuration options, see the{" "} .

- - {isCustomVM && ( - - -

- This blockchain uses a non-standard Virtual Machine ID. The Docker command automatically includes the AVAGO_VM_ALIASES_FILE_CONTENT environment variable with base64 encoded VM aliases configuration. -

-

- VM ID: {blockchainInfo.vmId}
- Aliases to: {SUBNET_EVM_VM_ID} -

-
-
- )} - - - -

To run multiple validator nodes on the same machine, ensure each node has:

-
    -
  • Unique container name (change --name parameter)
  • -
  • Different ports (modify AVAGO_HTTP_PORT and AVAGO_STAKING_PORT)
  • -
  • Separate data directories (change the local volume path ~/.avalanchego to a unique directory)
  • -
-

Example for second node: Use ports 9652/9653 (HTTP/staking), container name "avago2", and data directory "~/.avalanchego2"

- - - {/* Conditional steps based on nodeType */} - {nodeType === "public-rpc" && ( - - - - )} - {((nodeType === "public-rpc" && !!domain)) && ( - - - - )} - - {nodeType === "validator" && ( - -

Wait for the Node to Bootstrap

-

Your node will now bootstrap and sync the P-Chain and your L1. This process should take a few minutes. You can follow the process by checking the logs with the following command:

- - - - - -

The bootstrapping has three phases:

- -
    -
  • - Fetching the blocks of the P-Chain: - The node fetches all the P-Chain blocks. The eta field is giving the estimated remaining time for the fetching process. - -
  • -
  • - Executing the blocks of the P-Chain: - The node will sync the P-Chain and your L1. - -
  • -
-

After the P-Chain is fetched and executed the process is repeated for the tracked Subnet.

-
-
- - setNodeIsReady(checked)} - /> -
- )} - - {/* Show success message when node is ready for validator mode */} - {nodeIsReady && nodeType === "validator" && ( - -

Node Setup Complete

-

Your AvalancheGo node is now fully bootstrapped and ready to be used as a validator node.

- -
-
-
- - - -
-
-

- Node is ready for validation -

-

- Your node has successfully synced with the network and is ready to be added as a validator. -

-
-
-
-
- )} - )} - - - - - {chainAddedToWallet && ( - <> - - )} - - - + + + {configJson && !configJson.startsWith("Error:") && ( +
+ +
+ )} + ); }; diff --git a/components/toolbox/console/layer-1/node-config.ts b/components/toolbox/console/layer-1/node-config.ts new file mode 100644 index 00000000000..f559d5cf086 --- /dev/null +++ b/components/toolbox/console/layer-1/node-config.ts @@ -0,0 +1,192 @@ +// Node configuration generation for L1 Docker setup +import { SUBNET_EVM_VM_ID } from '@/constants/console'; +import { getContainerVersions } from '@/components/toolbox/utils/containerVersions'; + +/** + * Generates the Subnet-EVM chain configuration + * This configuration is saved to ~/.avalanchego/configs/chains//config.json + */ +export const generateChainConfig = ( + nodeType: 'validator' | 'public-rpc', + enableDebugTrace: boolean = false, + pruningEnabled: boolean = true, + minDelayTarget: number = 250, + trieCleanCache: number = 512, + trieDirtyCache: number = 512, + snapshotCache: number = 256, + commitInterval: number = 4096, + rpcGasCap: number = 50000000, + apiMaxBlocksPerRequest: number = 0, + allowUnfinalizedQueries: boolean = false +) => { + const isRPC = nodeType === 'public-rpc'; + + // Base configuration for all nodes + const config: any = { + "pruning-enabled": pruningEnabled, + "commit-interval": commitInterval, + "trie-clean-cache": trieCleanCache, + "trie-dirty-cache": trieDirtyCache, + "snapshot-cache": snapshotCache, + "rpc-gas-cap": rpcGasCap, + "log-level": enableDebugTrace ? "debug" : "info", + "metrics-expensive-enabled": true, + "accepted-cache-size": 32, + "min-delay-target": minDelayTarget + }; + + // Add warp API for cross-chain messaging (enabled by default for L1s) + config["warp-api-enabled"] = true; + + // Configure APIs based on node type + if (enableDebugTrace) { + config["eth-apis"] = [ + "eth", + "eth-filter", + "net", + "admin", + "web3", + "internal-eth", + "internal-blockchain", + "internal-transaction", + "internal-debug", + "internal-account", + "internal-personal", + "debug", + "debug-tracer", + "debug-file-tracer", + "debug-handler" + ]; + config["admin-api-enabled"] = true; + } else { + // Standard APIs + config["eth-apis"] = ["eth", "eth-filter", "net", "web3"]; + } + + // RPC-specific settings + if (isRPC) { + config["api-max-duration"] = 0; // No time limit + config["api-max-blocks-per-request"] = apiMaxBlocksPerRequest; + config["allow-unfinalized-queries"] = allowUnfinalizedQueries; + } + + return config; +}; + +/** + * Metadata object to store deployment information (separate from chain config) + */ +export const generateDeploymentMetadata = ( + subnetId: string, + blockchainId: string, + nodeType: 'validator' | 'public-rpc', + networkID: number, + vmId: string = SUBNET_EVM_VM_ID, + domain?: string +) => { + const isTestnet = networkID === 5; + const isCustomVM = vmId !== SUBNET_EVM_VM_ID; + + return { + "deployment": { + "network": isTestnet ? "fuji" : "mainnet", + "networkID": networkID, + "subnetId": subnetId, + "blockchainId": blockchainId, + "vmId": vmId, + "isCustomVM": isCustomVM, + "nodeType": nodeType, + "domain": domain || null, + "timestamp": new Date().toISOString() + } + }; +}; + +/** + * Generates base64-encoded chain config for environment variable + */ +export const encodeChainConfig = ( + blockchainId: string, + chainConfig: any +) => { + const chainConfigMap: Record = {}; + chainConfigMap[blockchainId] = { + "Config": btoa(JSON.stringify(chainConfig)), + "Upgrade": null + }; + return btoa(JSON.stringify(chainConfigMap)); +}; + +/** + * Generates a command to create the chain config file in the correct location + */ +export const generateConfigFileCommand = ( + blockchainId: string, + chainConfig: any +) => { + const configJson = JSON.stringify(chainConfig, null, 2); + const configPath = `~/.avalanchego/configs/chains/${blockchainId}`; + + // Escape single quotes in the JSON for the shell command + const escapedJson = configJson.replace(/'/g, "'\\''"); + + return `# Create the chain config directory and file +mkdir -p ${configPath} && cat > ${configPath}/config.json << 'EOF' +${configJson} +EOF`; +}; + +/** + * Generates the complete Docker command + * The chain config is read from the mounted volume at ~/.avalanchego/configs/chains//config.json + */ +export const generateDockerCommand = ( + subnetId: string, + blockchainId: string, + chainConfig: any, + nodeType: 'validator' | 'public-rpc', + networkID: number, + vmId: string = SUBNET_EVM_VM_ID +) => { + const isRPC = nodeType === 'public-rpc'; + const isTestnet = networkID === 5; // Fuji + const isCustomVM = vmId !== SUBNET_EVM_VM_ID; + const versions = getContainerVersions(isTestnet); + + const env: Record = { + AVAGO_PUBLIC_IP_RESOLUTION_SERVICE: "opendns", + AVAGO_HTTP_HOST: "0.0.0.0", + AVAGO_PARTIAL_SYNC_PRIMARY_NETWORK: "true", + AVAGO_TRACK_SUBNETS: subnetId + }; + + // Set network ID + if (networkID === 5) { + env.AVAGO_NETWORK_ID = "fuji"; + } + + // Configure RPC settings + if (isRPC) { + env.AVAGO_HTTP_ALLOWED_HOSTS = '"*"'; + } + + // Add VM aliases if custom VM + if (isCustomVM) { + const vmAliases = { + [vmId]: [SUBNET_EVM_VM_ID] + }; + env.AVAGO_VM_ALIASES_FILE_CONTENT = btoa(JSON.stringify(vmAliases, null, 2)); + } + + const chunks = [ + "docker run -it -d", + `--name avago`, + `-p ${isRPC ? "" : "127.0.0.1:"}9650:9650 -p 9651:9651`, + `-v ~/.avalanchego:/root/.avalanchego`, + ...Object.entries(env).map(([key, value]) => `-e ${key}=${value}`), + `avaplatform/subnet-evm_avalanchego:${versions['avaplatform/subnet-evm_avalanchego']}` + ]; + + return chunks.map(chunk => ` ${chunk}`).join(" \\\n").trim(); +}; + From 134a1980e276df8ecaf4fb04d2f329fcfcb3ba93 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 10 Nov 2025 18:48:57 -0500 Subject: [PATCH 2/7] fixes --- .../toolbox/components/ReverseProxySetup.tsx | 2 +- .../console/layer-1/AvalancheGoDockerL1.tsx | 107 +++++++++++++----- .../toolbox/console/layer-1/node-config.ts | 26 ++++- 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/components/toolbox/components/ReverseProxySetup.tsx b/components/toolbox/components/ReverseProxySetup.tsx index 74eec54b48d..b15b572aca0 100644 --- a/components/toolbox/components/ReverseProxySetup.tsx +++ b/components/toolbox/components/ReverseProxySetup.tsx @@ -50,7 +50,7 @@ const generateHealthCheckCommand = (domain: string, chainId: string) => { const processedDomain = nipify(domain); return `curl -X POST --data '{ - "jsonrpc":"2.0", "method":"eth_chainId", "params":[], "id":1 + "jsonrpc":"2.0", "method":"eth_blockNumber", "params":[], "id":1 }' -H 'content-type:application/json;' \\ https://${processedDomain}/ext/bc/${chainId}/rpc`; }; diff --git a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx index 8372e3b0869..fbe4dcc6b2e 100644 --- a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx +++ b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx @@ -12,6 +12,7 @@ import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; import { Button } from "../../components/Button"; import { Steps, Step } from "fumadocs-ui/components/steps"; import { SyntaxHighlightedJSON } from "../../components/genesis/SyntaxHighlightedJSON"; +import { ReverseProxySetup } from "../../components/ReverseProxySetup"; import { SUBNET_EVM_VM_ID } from "@/constants/console"; import { generateChainConfig, generateDockerCommand, generateConfigFileCommand } from "./node-config"; @@ -41,6 +42,10 @@ export default function AvalanchegoDocker() { const [apiMaxBlocksPerRequest, setApiMaxBlocksPerRequest] = useState(0); const [allowUnfinalizedQueries, setAllowUnfinalizedQueries] = useState(false); + // State and history + const [acceptedCacheSize, setAcceptedCacheSize] = useState(32); + const [transactionHistory, setTransactionHistory] = useState(0); + // Show advanced settings const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); @@ -67,20 +72,39 @@ export default function AvalanchegoDocker() { commitInterval, rpcGasCap, apiMaxBlocksPerRequest, - allowUnfinalizedQueries + allowUnfinalizedQueries, + acceptedCacheSize, + transactionHistory ); setConfigJson(JSON.stringify(config, null, 2)); } catch (error) { setConfigJson(`Error: ${(error as Error).message}`); } - }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, snapshotCache, commitInterval, rpcGasCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries]); + }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, snapshotCache, commitInterval, rpcGasCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, acceptedCacheSize, transactionHistory]); useEffect(() => { if (nodeType === "validator") { + // Validator node defaults - optimized for block production setDomain(""); setEnableDebugTrace(false); setPruningEnabled(true); - setMinDelayTarget(250); // Reset to default for L1 + setMinDelayTarget(250); // Fast block times for L1 + setAllowUnfinalizedQueries(false); + // Standard cache sizes for validators + setTrieCleanCache(512); + setTrieDirtyCache(512); + setSnapshotCache(256); + setAcceptedCacheSize(32); + setTransactionHistory(0); // Keep all tx history by default + } else if (nodeType === "public-rpc") { + // RPC node defaults - optimized for query performance + setAllowUnfinalizedQueries(true); // Enable real-time queries + // Larger caches for better RPC performance + setTrieCleanCache(1024); // 2x for better read performance + setTrieDirtyCache(1024); + setSnapshotCache(512); // 2x for snapshot queries + setAcceptedCacheSize(64); // Larger for more recent history + setTransactionHistory(0); // Keep all tx history by default for getLogs } }, [nodeType]); @@ -169,6 +193,8 @@ export default function AvalanchegoDocker() { setRpcGasCap(50000000); setApiMaxBlocksPerRequest(0); setAllowUnfinalizedQueries(false); + setAcceptedCacheSize(32); + setTransactionHistory(0); setShowAdvancedSettings(false); }; @@ -278,23 +304,7 @@ export default function AvalanchegoDocker() { This blockchain will be used for the RPC endpoint URL generation.

- )} - -
- - setDomain(e.target.value)} - placeholder="example.com" - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" - /> -

- If you plan to expose this RPC publicly, enter your domain name. -

-
+ )} )} @@ -381,6 +391,21 @@ export default function AvalanchegoDocker() { className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />
+ +
+ + setAcceptedCacheSize(Math.max(1, parseInt(e.target.value) || 1))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

+ Depth of accepted headers and logs cache +

+
@@ -417,6 +442,21 @@ export default function AvalanchegoDocker() { Maximum gas limit for RPC calls

+ +
+ + setTransactionHistory(Math.max(0, parseInt(e.target.value) || 0))} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

+ Maximum blocks from head to keep tx indices. 0 = no limit (archive mode) +

+
@@ -518,27 +558,31 @@ export default function AvalanchegoDocker() {
  • log-level: Logging level (trace, debug, info, warn, error, crit)
  • min-delay-target: Minimum delay between blocks in milliseconds
  • warp-api-enabled: Enables the Warp API for cross-chain messaging (ICM)
  • +
  • eth-apis: List of enabled Ethereum API namespaces
  • Cache Settings:

      -
    • trie-clean-cache: Size of the trie clean cache in MB (default: 512)
    • -
    • trie-dirty-cache: Size of the trie dirty cache in MB (default: 512)
    • -
    • snapshot-cache: Size of the snapshot disk layer clean cache in MB (default: 256)
    • +
    • trie-clean-cache: Size of the trie clean cache in MB (validator: 512, RPC: 1024)
    • +
    • trie-dirty-cache: Size of the trie dirty cache in MB (validator: 512, RPC: 1024)
    • +
    • snapshot-cache: Size of the snapshot disk layer clean cache in MB (validator: 256, RPC: 512)
    • +
    • accepted-cache-size: Depth to keep in accepted headers/logs cache (validator: 32, RPC: 64)

    Performance Settings:

      -
    • commit-interval: Interval at which to persist EVM and atomic tries (blocks, default: 4096)
    • +
    • commit-interval: Interval to persist EVM and atomic tries in blocks (default: 4096)
    • rpc-gas-cap: Maximum gas limit for RPC calls (default: 50,000,000)
    • +
    • transaction-history: Max blocks from head to keep tx indices. 0 = archive mode (all history)
    {isRPC && ( <>

    RPC-Specific Settings:

      +
    • api-max-duration: Maximum duration for API calls (0 = no limit)
    • api-max-blocks-per-request: Maximum blocks per getLogs request (0 = no limit)
    • -
    • allow-unfinalized-queries: Allows queries for unfinalized blocks
    • +
    • allow-unfinalized-queries: Allows queries for unfinalized/pending blocks
    )} @@ -547,7 +591,7 @@ export default function AvalanchegoDocker() { <>

    Debug Settings:

      -
    • eth-apis: Extended list of Ethereum APIs including debug and tracing capabilities
    • +
    • eth-apis: Extended APIs including debug-tracer, debug-file-tracer, internal-* APIs
    • admin-api-enabled: Enables administrative operations API
    @@ -629,6 +673,17 @@ export default function AvalanchegoDocker() {
    + + {nodeType === "public-rpc" && ( + + + + )} )}
    diff --git a/components/toolbox/console/layer-1/node-config.ts b/components/toolbox/console/layer-1/node-config.ts index f559d5cf086..b5c52b741e5 100644 --- a/components/toolbox/console/layer-1/node-config.ts +++ b/components/toolbox/console/layer-1/node-config.ts @@ -17,7 +17,9 @@ export const generateChainConfig = ( commitInterval: number = 4096, rpcGasCap: number = 50000000, apiMaxBlocksPerRequest: number = 0, - allowUnfinalizedQueries: boolean = false + allowUnfinalizedQueries: boolean = false, + acceptedCacheSize: number = 32, + transactionHistory: number = 0 ) => { const isRPC = nodeType === 'public-rpc'; @@ -31,7 +33,7 @@ export const generateChainConfig = ( "rpc-gas-cap": rpcGasCap, "log-level": enableDebugTrace ? "debug" : "info", "metrics-expensive-enabled": true, - "accepted-cache-size": 32, + "accepted-cache-size": acceptedCacheSize, "min-delay-target": minDelayTarget }; @@ -59,8 +61,16 @@ export const generateChainConfig = ( ]; config["admin-api-enabled"] = true; } else { - // Standard APIs - config["eth-apis"] = ["eth", "eth-filter", "net", "web3"]; + // Standard APIs (includes internal APIs required for basic eth methods) + config["eth-apis"] = [ + "eth", + "eth-filter", + "net", + "web3", + "internal-eth", + "internal-blockchain", + "internal-transaction" + ]; } // RPC-specific settings @@ -70,6 +80,11 @@ export const generateChainConfig = ( config["allow-unfinalized-queries"] = allowUnfinalizedQueries; } + // Transaction history (0 = no limit, keeps all tx indices) + if (transactionHistory > 0) { + config["transaction-history"] = transactionHistory; + } + return config; }; @@ -157,7 +172,8 @@ export const generateDockerCommand = ( AVAGO_PUBLIC_IP_RESOLUTION_SERVICE: "opendns", AVAGO_HTTP_HOST: "0.0.0.0", AVAGO_PARTIAL_SYNC_PRIMARY_NETWORK: "true", - AVAGO_TRACK_SUBNETS: subnetId + AVAGO_TRACK_SUBNETS: subnetId, + AVAGO_CHAIN_CONFIG_DIR: "/root/.avalanchego/configs/chains" }; // Set network ID From b77de9ef55388f276cc77efc2cef9b052f10f597 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 10 Nov 2025 19:47:28 -0500 Subject: [PATCH 3/7] code highlighting + nits --- .../console/layer-1/AvalancheGoDockerL1.tsx | 655 +++++++++++++----- .../toolbox/console/layer-1/node-config.ts | 59 +- .../layer-1/useNodeConfigHighlighting.ts | 78 +++ 3 files changed, 626 insertions(+), 166 deletions(-) create mode 100644 components/toolbox/console/layer-1/useNodeConfigHighlighting.ts diff --git a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx index fbe4dcc6b2e..fcfbfdf6f3d 100644 --- a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx +++ b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx @@ -13,10 +13,13 @@ import { Button } from "../../components/Button"; import { Steps, Step } from "fumadocs-ui/components/steps"; import { SyntaxHighlightedJSON } from "../../components/genesis/SyntaxHighlightedJSON"; import { ReverseProxySetup } from "../../components/ReverseProxySetup"; +import { GenesisHighlightProvider, useGenesisHighlight } from "../../components/genesis/GenesisHighlightContext"; import { SUBNET_EVM_VM_ID } from "@/constants/console"; import { generateChainConfig, generateDockerCommand, generateConfigFileCommand } from "./node-config"; +import { useNodeConfigHighlighting } from "./useNodeConfigHighlighting"; -export default function AvalanchegoDocker() { +function AvalanchegoDockerInner() { + const { setHighlightPath, clearHighlight, highlightPath } = useGenesisHighlight(); const [chainId, setChainId] = useState(""); const [subnetId, setSubnetId] = useState(""); const [subnet, setSubnet] = useState(null); @@ -30,22 +33,42 @@ export default function AvalanchegoDocker() { const [selectedRPCBlockchainId, setSelectedRPCBlockchainId] = useState(""); const [minDelayTarget, setMinDelayTarget] = useState(250); const [configJson, setConfigJson] = useState(""); - + // Advanced cache settings const [trieCleanCache, setTrieCleanCache] = useState(512); const [trieDirtyCache, setTrieDirtyCache] = useState(512); + const [trieDirtyCommitTarget, setTrieDirtyCommitTarget] = useState(20); + const [triePrefetcherParallelism, setTriePrefetcherParallelism] = useState(16); const [snapshotCache, setSnapshotCache] = useState(256); const [commitInterval, setCommitInterval] = useState(4096); - + const [stateSyncServerTrieCache, setStateSyncServerTrieCache] = useState(64); + // API settings const [rpcGasCap, setRpcGasCap] = useState(50000000); + const [rpcTxFeeCap, setRpcTxFeeCap] = useState(100); const [apiMaxBlocksPerRequest, setApiMaxBlocksPerRequest] = useState(0); const [allowUnfinalizedQueries, setAllowUnfinalizedQueries] = useState(false); - + const [batchRequestLimit, setBatchRequestLimit] = useState(1000); + const [batchResponseMaxSize, setBatchResponseMaxSize] = useState(25000000); + // State and history const [acceptedCacheSize, setAcceptedCacheSize] = useState(32); const [transactionHistory, setTransactionHistory] = useState(0); - + const [stateSyncEnabled, setStateSyncEnabled] = useState(false); + const [skipTxIndexing, setSkipTxIndexing] = useState(false); + + // Transaction settings + const [preimagesEnabled, setPreimagesEnabled] = useState(false); + const [localTxsEnabled, setLocalTxsEnabled] = useState(false); + + // Gossip settings (validator specific) + const [pushGossipNumValidators, setPushGossipNumValidators] = useState(100); + const [pushGossipPercentStake, setPushGossipPercentStake] = useState(0.9); + + // Profiling + const [continuousProfilerDir, setContinuousProfilerDir] = useState(""); + const [continuousProfilerFrequency, setContinuousProfilerFrequency] = useState("15m"); + // Show advanced settings const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); @@ -53,6 +76,9 @@ export default function AvalanchegoDocker() { const isRPC = nodeType === "public-rpc"; + // Get highlighted lines for JSON preview + const highlightedLines = useNodeConfigHighlighting(highlightPath, configJson); + // Generate Subnet-EVM chain configuration JSON when parameters change useEffect(() => { if (!subnetId || !chainId || !blockchainInfo) { @@ -68,19 +94,33 @@ export default function AvalanchegoDocker() { minDelayTarget, trieCleanCache, trieDirtyCache, + trieDirtyCommitTarget, + triePrefetcherParallelism, snapshotCache, commitInterval, + stateSyncServerTrieCache, rpcGasCap, + rpcTxFeeCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, + batchRequestLimit, + batchResponseMaxSize, acceptedCacheSize, - transactionHistory + transactionHistory, + stateSyncEnabled, + skipTxIndexing, + preimagesEnabled, + localTxsEnabled, + pushGossipNumValidators, + pushGossipPercentStake, + continuousProfilerDir, + continuousProfilerFrequency ); setConfigJson(JSON.stringify(config, null, 2)); } catch (error) { setConfigJson(`Error: ${(error as Error).message}`); } - }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, snapshotCache, commitInterval, rpcGasCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, acceptedCacheSize, transactionHistory]); + }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, trieDirtyCommitTarget, triePrefetcherParallelism, snapshotCache, commitInterval, stateSyncServerTrieCache, rpcGasCap, rpcTxFeeCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, batchRequestLimit, batchResponseMaxSize, acceptedCacheSize, transactionHistory, stateSyncEnabled, skipTxIndexing, preimagesEnabled, localTxsEnabled, pushGossipNumValidators, pushGossipPercentStake, continuousProfilerDir, continuousProfilerFrequency]); useEffect(() => { if (nodeType === "validator") { @@ -188,13 +228,27 @@ export default function AvalanchegoDocker() { setConfigJson(""); setTrieCleanCache(512); setTrieDirtyCache(512); + setTrieDirtyCommitTarget(20); + setTriePrefetcherParallelism(16); setSnapshotCache(256); setCommitInterval(4096); + setStateSyncServerTrieCache(64); setRpcGasCap(50000000); + setRpcTxFeeCap(100); setApiMaxBlocksPerRequest(0); setAllowUnfinalizedQueries(false); + setBatchRequestLimit(1000); + setBatchResponseMaxSize(25000000); setAcceptedCacheSize(32); setTransactionHistory(0); + setStateSyncEnabled(false); + setSkipTxIndexing(false); + setPreimagesEnabled(false); + setLocalTxsEnabled(false); + setPushGossipNumValidators(100); + setPushGossipPercentStake(0.9); + setContinuousProfilerDir(""); + setContinuousProfilerFrequency("15m"); setShowAdvancedSettings(false); }; @@ -202,44 +256,44 @@ export default function AvalanchegoDocker() { const isCustomVM = blockchainInfo && blockchainInfo.vmId !== SUBNET_EVM_VM_ID; return ( - - - + githubUrl="https://github.com/ava-labs/builders-hub/edit/master/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx" + > + +

    Select L1

    Enter the Avalanche Subnet ID of the L1 you want to run a node for

    - + - {subnet && subnet.blockchains && subnet.blockchains.length > 0 && ( + {subnet && subnet.blockchains && subnet.blockchains.length > 0 && (
    - {subnet.blockchains.map((blockchain: { blockchainId: string; blockchainName: string; createBlockTimestamp: number; createBlockNumber: string; vmId: string; subnetId: string; evmChainId: number }) => ( - - ))} -
    - )} -
    + {subnet.blockchains.map((blockchain: { blockchainId: string; blockchainName: string; createBlockTimestamp: number; createBlockNumber: string; vmId: string; subnetId: string; evmChainId: number }) => ( + + ))} + + )} +
    - {subnetId && blockchainInfo && ( - <> - + {subnetId && blockchainInfo && ( + <> +

    Configure Node Settings

    @@ -260,7 +314,7 @@ export default function AvalanchegoDocker() { {nodeType === "public-rpc" && ( <> -
    +
    setHighlightPath('ethApis')} onMouseLeave={clearHighlight}>
    -
    +
    setHighlightPath('pruning')} onMouseLeave={clearHighlight}>
    )} )} {nodeType === "validator" && ( -
    +
    setHighlightPath('minDelayTarget')} onMouseLeave={clearHighlight}> @@ -320,6 +374,8 @@ export default function AvalanchegoDocker() { const value = Math.min(2000, Math.max(0, parseInt(e.target.value) || 0)); setMinDelayTarget(value); }} + onFocus={() => setHighlightPath('minDelayTarget')} + onBlur={clearHighlight} min="0" max="2000" className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" @@ -352,11 +408,31 @@ export default function AvalanchegoDocker() { {showAdvancedSettings && (
    + + For advanced configuration options, see the{" "} + + AvalancheGo configuration + {" "} + and{" "} + + Subnet-EVM configuration + documentation. +

    Cache Settings

    - +
    -
    +
    setHighlightPath('trieCleanCache')} onMouseLeave={clearHighlight}> @@ -364,11 +440,13 @@ export default function AvalanchegoDocker() { type="number" value={trieCleanCache} onChange={(e) => setTrieCleanCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('trieCleanCache')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />
    -
    +
    setHighlightPath('trieDirtyCache')} onMouseLeave={clearHighlight}> @@ -376,11 +454,13 @@ export default function AvalanchegoDocker() { type="number" value={trieDirtyCache} onChange={(e) => setTrieDirtyCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('trieDirtyCache')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />
    -
    +
    setHighlightPath('snapshotCache')} onMouseLeave={clearHighlight}> @@ -388,11 +468,13 @@ export default function AvalanchegoDocker() { type="number" value={snapshotCache} onChange={(e) => setSnapshotCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('snapshotCache')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />
    -
    +
    setHighlightPath('acceptedCacheSize')} onMouseLeave={clearHighlight}> @@ -400,20 +482,73 @@ export default function AvalanchegoDocker() { type="number" value={acceptedCacheSize} onChange={(e) => setAcceptedCacheSize(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('acceptedCacheSize')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />

    Depth of accepted headers and logs cache

    + +
    setHighlightPath('trieDirtyCommitTarget')} onMouseLeave={clearHighlight}> + + setTrieDirtyCommitTarget(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('trieDirtyCommitTarget')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Memory limit before commit +

    +
    + +
    setHighlightPath('triePrefetcherParallelism')} onMouseLeave={clearHighlight}> + + setTriePrefetcherParallelism(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('triePrefetcherParallelism')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max concurrent disk reads +

    +
    + +
    setHighlightPath('stateSyncServerTrieCache')} onMouseLeave={clearHighlight}> + + setStateSyncServerTrieCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('stateSyncServerTrieCache')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Trie cache for state sync server +

    +

    Performance Settings

    - +
    -
    +
    setHighlightPath('commitInterval')} onMouseLeave={clearHighlight}> @@ -421,6 +556,8 @@ export default function AvalanchegoDocker() { type="number" value={commitInterval} onChange={(e) => setCommitInterval(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('commitInterval')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />

    @@ -428,7 +565,7 @@ export default function AvalanchegoDocker() {

    -
    +
    setHighlightPath('rpcGasCap')} onMouseLeave={clearHighlight}> @@ -436,6 +573,8 @@ export default function AvalanchegoDocker() { type="number" value={rpcGasCap} onChange={(e) => setRpcGasCap(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('rpcGasCap')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />

    @@ -443,7 +582,70 @@ export default function AvalanchegoDocker() {

    -
    +
    setHighlightPath('rpcTxFeeCap')} onMouseLeave={clearHighlight}> + + setRpcTxFeeCap(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('rpcTxFeeCap')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Maximum transaction fee cap +

    +
    +
    +
    + +
    +

    API Limits

    + +
    +
    setHighlightPath('batchRequestLimit')} onMouseLeave={clearHighlight}> + + setBatchRequestLimit(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('batchRequestLimit')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max batched requests (0 = no limit) +

    +
    + +
    setHighlightPath('batchResponseMaxSize')} onMouseLeave={clearHighlight}> + + setBatchResponseMaxSize(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('batchResponseMaxSize')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max batch response size (default: 25MB) +

    +
    +
    +
    + +
    +

    Transaction & State

    + +
    +
    setHighlightPath('transactionHistory')} onMouseLeave={clearHighlight}> @@ -451,21 +653,144 @@ export default function AvalanchegoDocker() { type="number" value={transactionHistory} onChange={(e) => setTransactionHistory(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('transactionHistory')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />

    - Maximum blocks from head to keep tx indices. 0 = no limit (archive mode) + Max blocks to keep tx indices. 0 = archive mode (all history) +

    +
    + +
    setHighlightPath('skipTxIndexing')} onMouseLeave={clearHighlight}> + +

    + Disable tx indexing entirely (saves disk space) +

    +
    + +
    setHighlightPath('stateSyncEnabled')} onMouseLeave={clearHighlight}> + +

    + Fast sync from state summary +

    +
    + +
    setHighlightPath('preimagesEnabled')} onMouseLeave={clearHighlight}> + +

    + Record preimages (uses more disk) +

    +
    + +
    setHighlightPath('localTxsEnabled')} onMouseLeave={clearHighlight}> + +

    + Treat local account txs as local

    + {nodeType === "validator" && ( +
    +

    Gossip Settings (Validator)

    + +
    +
    setHighlightPath('pushGossipNumValidators')} onMouseLeave={clearHighlight}> + + setPushGossipNumValidators(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('pushGossipNumValidators')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Number of validators to push gossip to +

    +
    + +
    setHighlightPath('pushGossipPercentStake')} onMouseLeave={clearHighlight}> + + setPushGossipPercentStake(Math.min(1, Math.max(0, parseFloat(e.target.value) || 0)))} + onFocus={() => setHighlightPath('pushGossipPercentStake')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Percentage of total stake to gossip to (0-1) +

    +
    +
    +
    + )} + {isRPC && (
    -

    RPC Settings

    - +

    RPC-Specific Settings

    +
    -
    +
    setHighlightPath('apiMaxBlocksPerRequest')} onMouseLeave={clearHighlight}> @@ -473,6 +798,8 @@ export default function AvalanchegoDocker() { type="number" value={apiMaxBlocksPerRequest} onChange={(e) => setApiMaxBlocksPerRequest(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('apiMaxBlocksPerRequest')} + onBlur={clearHighlight} className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" />

    @@ -480,12 +807,14 @@ export default function AvalanchegoDocker() {

    -
    +
    setHighlightPath('allowUnfinalizedQueries')} onMouseLeave={clearHighlight}>

    - Allows queries for unfinalized blocks + Allows queries for unfinalized/pending blocks

    )} + +
    +

    Profiling (Optional)

    + +
    +
    setHighlightPath('continuousProfilerDir')} onMouseLeave={clearHighlight}> + + setContinuousProfilerDir(e.target.value)} + onFocus={() => setHighlightPath('continuousProfilerDir')} + onBlur={clearHighlight} + placeholder="./profiles (leave empty to disable)" + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Directory for continuous profiler output +

    +
    + + {continuousProfilerDir && ( +
    setHighlightPath('continuousProfilerFrequency')} onMouseLeave={clearHighlight}> + + setContinuousProfilerFrequency(e.target.value)} + onFocus={() => setHighlightPath('continuousProfilerFrequency')} + onBlur={clearHighlight} + placeholder="15m" + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + How often to create profiles (e.g., 15m, 1h) +

    +
    + )} +
    +
    )}
    @@ -514,7 +887,7 @@ export default function AvalanchegoDocker() { {configJson && !configJson.startsWith("Error:") ? ( ) : (
    @@ -525,16 +898,16 @@ export default function AvalanchegoDocker() {
    - + - +

    Create Configuration File

    Run this command on your server to create the Subnet-EVM chain configuration file:

    - { try { const config = JSON.parse(configJson); @@ -542,63 +915,33 @@ export default function AvalanchegoDocker() { } catch { return "# Error generating config file command"; } - })()} + })()} /> - +

    This creates the configuration file at ~/.avalanchego/configs/chains/{chainId}/config.json

    - +

    + Read the documentation for more information on the configuration options. {" "} + + AvalancheGo configuration + + {" "}and{" "} + + Subnet-EVM configuration + +

    - -
    -

    Basic Settings:

    -
      -
    • pruning-enabled: Enables state pruning to save disk space
    • -
    • log-level: Logging level (trace, debug, info, warn, error, crit)
    • -
    • min-delay-target: Minimum delay between blocks in milliseconds
    • -
    • warp-api-enabled: Enables the Warp API for cross-chain messaging (ICM)
    • -
    • eth-apis: List of enabled Ethereum API namespaces
    • -
    - -

    Cache Settings:

    -
      -
    • trie-clean-cache: Size of the trie clean cache in MB (validator: 512, RPC: 1024)
    • -
    • trie-dirty-cache: Size of the trie dirty cache in MB (validator: 512, RPC: 1024)
    • -
    • snapshot-cache: Size of the snapshot disk layer clean cache in MB (validator: 256, RPC: 512)
    • -
    • accepted-cache-size: Depth to keep in accepted headers/logs cache (validator: 32, RPC: 64)
    • -
    - -

    Performance Settings:

    -
      -
    • commit-interval: Interval to persist EVM and atomic tries in blocks (default: 4096)
    • -
    • rpc-gas-cap: Maximum gas limit for RPC calls (default: 50,000,000)
    • -
    • transaction-history: Max blocks from head to keep tx indices. 0 = archive mode (all history)
    • -
    - - {isRPC && ( - <> -

    RPC-Specific Settings:

    -
      -
    • api-max-duration: Maximum duration for API calls (0 = no limit)
    • -
    • api-max-blocks-per-request: Maximum blocks per getLogs request (0 = no limit)
    • -
    • allow-unfinalized-queries: Allows queries for unfinalized/pending blocks
    • -
    - - )} - - {enableDebugTrace && ( - <> -

    Debug Settings:

    -
      -
    • eth-apis: Extended APIs including debug-tracer, debug-file-tracer, internal-* APIs
    • -
    • admin-api-enabled: Enables administrative operations API
    • -
    - - )} -
    -
    - {isCustomVM && (

    @@ -619,8 +962,8 @@ export default function AvalanchegoDocker() { Start the node using Docker:

    - { try { const config = JSON.parse(configJson); @@ -636,10 +979,10 @@ export default function AvalanchegoDocker() { } catch { return "# Error generating Docker command"; } - })()} + })()} /> -

    +

    The container will read the config from ~/.avalanchego/configs/chains/{chainId}/config.json via the mounted volume.

    @@ -657,33 +1000,19 @@ export default function AvalanchegoDocker() {

    Monitor your node with:

    +
    +
    - -

    - For advanced node configuration options, see the{" "} - - AvalancheGo configuration flags documentation - . -

    -
    - -
    - - {nodeType === "public-rpc" && ( - - + - - )} + showHealthCheck={true} + /> + + )} )} @@ -697,4 +1026,12 @@ export default function AvalanchegoDocker() { )} ); -}; +} + +export default function AvalanchegoDocker() { + return ( + + + + ); +} diff --git a/components/toolbox/console/layer-1/node-config.ts b/components/toolbox/console/layer-1/node-config.ts index b5c52b741e5..3adae6e9bd7 100644 --- a/components/toolbox/console/layer-1/node-config.ts +++ b/components/toolbox/console/layer-1/node-config.ts @@ -13,13 +13,27 @@ export const generateChainConfig = ( minDelayTarget: number = 250, trieCleanCache: number = 512, trieDirtyCache: number = 512, + trieDirtyCommitTarget: number = 20, + triePrefetcherParallelism: number = 16, snapshotCache: number = 256, commitInterval: number = 4096, + stateSyncServerTrieCache: number = 64, rpcGasCap: number = 50000000, + rpcTxFeeCap: number = 100, apiMaxBlocksPerRequest: number = 0, allowUnfinalizedQueries: boolean = false, + batchRequestLimit: number = 1000, + batchResponseMaxSize: number = 25000000, acceptedCacheSize: number = 32, - transactionHistory: number = 0 + transactionHistory: number = 0, + stateSyncEnabled: boolean = false, + skipTxIndexing: boolean = false, + preimagesEnabled: boolean = false, + localTxsEnabled: boolean = false, + pushGossipNumValidators: number = 100, + pushGossipPercentStake: number = 0.9, + continuousProfilerDir: string = "", + continuousProfilerFrequency: string = "15m" ) => { const isRPC = nodeType === 'public-rpc'; @@ -29,17 +43,41 @@ export const generateChainConfig = ( "commit-interval": commitInterval, "trie-clean-cache": trieCleanCache, "trie-dirty-cache": trieDirtyCache, + "trie-dirty-commit-target": trieDirtyCommitTarget, + "trie-prefetcher-parallelism": triePrefetcherParallelism, "snapshot-cache": snapshotCache, + "state-sync-server-trie-cache": stateSyncServerTrieCache, "rpc-gas-cap": rpcGasCap, + "rpc-tx-fee-cap": rpcTxFeeCap, "log-level": enableDebugTrace ? "debug" : "info", "metrics-expensive-enabled": true, "accepted-cache-size": acceptedCacheSize, - "min-delay-target": minDelayTarget + "min-delay-target": minDelayTarget, + "batch-request-limit": batchRequestLimit, + "batch-response-max-size": batchResponseMaxSize }; // Add warp API for cross-chain messaging (enabled by default for L1s) config["warp-api-enabled"] = true; + // State sync configuration + config["state-sync-enabled"] = stateSyncEnabled; + + // Transaction indexing + if (skipTxIndexing) { + config["skip-tx-indexing"] = true; + } else if (transactionHistory > 0) { + config["transaction-history"] = transactionHistory; + } + + // Transaction settings + if (preimagesEnabled) { + config["preimages-enabled"] = true; + } + if (localTxsEnabled) { + config["local-txs-enabled"] = true; + } + // Configure APIs based on node type if (enableDebugTrace) { config["eth-apis"] = [ @@ -80,9 +118,16 @@ export const generateChainConfig = ( config["allow-unfinalized-queries"] = allowUnfinalizedQueries; } - // Transaction history (0 = no limit, keeps all tx indices) - if (transactionHistory > 0) { - config["transaction-history"] = transactionHistory; + // Gossip settings (primarily for validators) + if (nodeType === 'validator') { + config["push-gossip-num-validators"] = pushGossipNumValidators; + config["push-gossip-percent-stake"] = pushGossipPercentStake; + } + + // Continuous profiling (if enabled) + if (continuousProfilerDir) { + config["continuous-profiler-dir"] = continuousProfilerDir; + config["continuous-profiler-frequency"] = continuousProfilerFrequency; } return config; @@ -141,10 +186,10 @@ export const generateConfigFileCommand = ( ) => { const configJson = JSON.stringify(chainConfig, null, 2); const configPath = `~/.avalanchego/configs/chains/${blockchainId}`; - + // Escape single quotes in the JSON for the shell command const escapedJson = configJson.replace(/'/g, "'\\''"); - + return `# Create the chain config directory and file mkdir -p ${configPath} && cat > ${configPath}/config.json << 'EOF' ${configJson} diff --git a/components/toolbox/console/layer-1/useNodeConfigHighlighting.ts b/components/toolbox/console/layer-1/useNodeConfigHighlighting.ts new file mode 100644 index 00000000000..3cdba795c99 --- /dev/null +++ b/components/toolbox/console/layer-1/useNodeConfigHighlighting.ts @@ -0,0 +1,78 @@ +import { useMemo } from 'react'; + +export function useNodeConfigHighlighting(highlightPath: string | null, configJson: string) { + return useMemo(() => { + if (!highlightPath || !configJson) return []; + + const lines = configJson.split('\n'); + const highlighted: number[] = []; + + // Map of highlight paths to their JSON keys + const fieldMap: Record = { + 'pruning': 'pruning-enabled', + 'minDelayTarget': 'min-delay-target', + 'trieCleanCache': 'trie-clean-cache', + 'trieDirtyCache': 'trie-dirty-cache', + 'trieDirtyCommitTarget': 'trie-dirty-commit-target', + 'triePrefetcherParallelism': 'trie-prefetcher-parallelism', + 'snapshotCache': 'snapshot-cache', + 'acceptedCacheSize': 'accepted-cache-size', + 'stateSyncServerTrieCache': 'state-sync-server-trie-cache', + 'commitInterval': 'commit-interval', + 'rpcGasCap': 'rpc-gas-cap', + 'rpcTxFeeCap': 'rpc-tx-fee-cap', + 'transactionHistory': 'transaction-history', + 'apiMaxBlocksPerRequest': 'api-max-blocks-per-request', + 'allowUnfinalizedQueries': 'allow-unfinalized-queries', + 'batchRequestLimit': 'batch-request-limit', + 'batchResponseMaxSize': 'batch-response-max-size', + 'skipTxIndexing': 'skip-tx-indexing', + 'stateSyncEnabled': 'state-sync-enabled', + 'preimagesEnabled': 'preimages-enabled', + 'localTxsEnabled': 'local-txs-enabled', + 'pushGossipNumValidators': 'push-gossip-num-validators', + 'pushGossipPercentStake': 'push-gossip-percent-stake', + 'continuousProfilerDir': 'continuous-profiler-dir', + 'continuousProfilerFrequency': 'continuous-profiler-frequency', + 'logLevel': 'log-level', + 'warpApi': 'warp-api-enabled', + 'ethApis': 'eth-apis', + 'adminApi': 'admin-api-enabled', + 'metricsExpensive': 'metrics-expensive-enabled', + // Additional fields that might be in config + 'apiMaxDuration': 'api-max-duration' + }; + + const fieldName = fieldMap[highlightPath]; + if (fieldName) { + const searchKey = typeof fieldName === 'string' ? fieldName : fieldName[0]; + const idx = lines.findIndex(line => line.includes(`"${searchKey}"`)); + if (idx >= 0) { + highlighted.push(idx + 1); + + // For arrays, highlight multiple lines + if (searchKey === 'eth-apis') { + let bracketCount = 0; + let inArray = false; + for (let i = idx; i < lines.length && i < idx + 30; i++) { + const line = lines[i]; + if (line.includes('[')) { + inArray = true; + bracketCount++; + } + if (inArray) { + highlighted.push(i + 1); + if (line.includes(']')) { + bracketCount--; + if (bracketCount === 0) break; + } + } + } + } + } + } + + return [...new Set(highlighted)]; + }, [highlightPath, configJson]); +} + From 360b8d80492570384ac3776d620825c8443deb5b Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 17 Nov 2025 17:00:40 -0500 Subject: [PATCH 4/7] update config defaults --- .../console/layer-1/AvalancheGoDockerL1.tsx | 4 +- .../toolbox/console/layer-1/node-config.ts | 128 ++++++++++++++---- 2 files changed, 100 insertions(+), 32 deletions(-) diff --git a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx index fcfbfdf6f3d..c9ed95c543a 100644 --- a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx +++ b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx @@ -48,7 +48,7 @@ function AvalanchegoDockerInner() { const [rpcTxFeeCap, setRpcTxFeeCap] = useState(100); const [apiMaxBlocksPerRequest, setApiMaxBlocksPerRequest] = useState(0); const [allowUnfinalizedQueries, setAllowUnfinalizedQueries] = useState(false); - const [batchRequestLimit, setBatchRequestLimit] = useState(1000); + const [batchRequestLimit, setBatchRequestLimit] = useState(0); // 0 = no limit (default) const [batchResponseMaxSize, setBatchResponseMaxSize] = useState(25000000); // State and history @@ -237,7 +237,7 @@ function AvalanchegoDockerInner() { setRpcTxFeeCap(100); setApiMaxBlocksPerRequest(0); setAllowUnfinalizedQueries(false); - setBatchRequestLimit(1000); + setBatchRequestLimit(0); setBatchResponseMaxSize(25000000); setAcceptedCacheSize(32); setTransactionHistory(0); diff --git a/components/toolbox/console/layer-1/node-config.ts b/components/toolbox/console/layer-1/node-config.ts index 3adae6e9bd7..e2de06162e9 100644 --- a/components/toolbox/console/layer-1/node-config.ts +++ b/components/toolbox/console/layer-1/node-config.ts @@ -2,8 +2,35 @@ import { SUBNET_EVM_VM_ID } from '@/constants/console'; import { getContainerVersions } from '@/components/toolbox/utils/containerVersions'; +// Subnet-EVM default configuration values +// Reference: https://build.avax.network/docs/nodes/chain-configs/subnet-evm +const SUBNET_EVM_DEFAULTS = { + "pruning-enabled": true, + "commit-interval": 4096, + "trie-clean-cache": 512, + "trie-dirty-cache": 512, + "trie-dirty-commit-target": 20, + "trie-prefetcher-parallelism": 16, + "snapshot-cache": 256, + "state-sync-server-trie-cache": 64, + "rpc-gas-cap": 50000000, + "rpc-tx-fee-cap": 100, + "log-level": "info", + "metrics-expensive-enabled": false, + "accepted-cache-size": 32, + "batch-request-limit": 0, + "batch-response-max-size": 25000000, + "state-sync-enabled": false, + "allow-unfinalized-queries": false, + "api-max-duration": 0, + "api-max-blocks-per-request": 0, + // Default eth-apis + "eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction"], +}; + /** * Generates the Subnet-EVM chain configuration + * Only includes values that differ from defaults * This configuration is saved to ~/.avalanchego/configs/chains//config.json */ export const generateChainConfig = ( @@ -22,7 +49,7 @@ export const generateChainConfig = ( rpcTxFeeCap: number = 100, apiMaxBlocksPerRequest: number = 0, allowUnfinalizedQueries: boolean = false, - batchRequestLimit: number = 1000, + batchRequestLimit: number = 0, batchResponseMaxSize: number = 25000000, acceptedCacheSize: number = 32, transactionHistory: number = 0, @@ -36,41 +63,74 @@ export const generateChainConfig = ( continuousProfilerFrequency: string = "15m" ) => { const isRPC = nodeType === 'public-rpc'; + const config: any = {}; - // Base configuration for all nodes - const config: any = { - "pruning-enabled": pruningEnabled, - "commit-interval": commitInterval, - "trie-clean-cache": trieCleanCache, - "trie-dirty-cache": trieDirtyCache, - "trie-dirty-commit-target": trieDirtyCommitTarget, - "trie-prefetcher-parallelism": triePrefetcherParallelism, - "snapshot-cache": snapshotCache, - "state-sync-server-trie-cache": stateSyncServerTrieCache, - "rpc-gas-cap": rpcGasCap, - "rpc-tx-fee-cap": rpcTxFeeCap, - "log-level": enableDebugTrace ? "debug" : "info", - "metrics-expensive-enabled": true, - "accepted-cache-size": acceptedCacheSize, - "min-delay-target": minDelayTarget, - "batch-request-limit": batchRequestLimit, - "batch-response-max-size": batchResponseMaxSize + // Helper function to add config only if it differs from default + const addIfNotDefault = (key: string, value: any, defaultValue?: any) => { + const defaultVal = defaultValue !== undefined ? defaultValue : SUBNET_EVM_DEFAULTS[key as keyof typeof SUBNET_EVM_DEFAULTS]; + + // For arrays, do a deep comparison + if (Array.isArray(value) && Array.isArray(defaultVal)) { + if (JSON.stringify(value) !== JSON.stringify(defaultVal)) { + config[key] = value; + } + } else if (value !== defaultVal) { + config[key] = value; + } }; - // Add warp API for cross-chain messaging (enabled by default for L1s) + // Always include pruning-enabled for explicitness in L1 node setup + config["pruning-enabled"] = pruningEnabled; + + // Cache settings - only add if different from defaults + addIfNotDefault("trie-clean-cache", trieCleanCache); + addIfNotDefault("trie-dirty-cache", trieDirtyCache); + addIfNotDefault("trie-dirty-commit-target", trieDirtyCommitTarget); + addIfNotDefault("trie-prefetcher-parallelism", triePrefetcherParallelism); + addIfNotDefault("snapshot-cache", snapshotCache); + addIfNotDefault("state-sync-server-trie-cache", stateSyncServerTrieCache); + addIfNotDefault("accepted-cache-size", acceptedCacheSize); + addIfNotDefault("commit-interval", commitInterval); + + // Performance settings + addIfNotDefault("rpc-gas-cap", rpcGasCap); + addIfNotDefault("rpc-tx-fee-cap", rpcTxFeeCap); + + // Logging - only add if debug is enabled + if (enableDebugTrace) { + config["log-level"] = "debug"; + } + + // Metrics - only add if enabled (default is false) + if (true) { // We always want expensive metrics enabled + config["metrics-expensive-enabled"] = true; + } + + // Min delay target - only add if non-zero (default is 0, meaning no minimum delay) + if (minDelayTarget > 0) { + config["min-delay-target"] = minDelayTarget; + } + + // Batch limits - only add if different from defaults + addIfNotDefault("batch-request-limit", batchRequestLimit); + addIfNotDefault("batch-response-max-size", batchResponseMaxSize); + + // Warp API - typically enabled for L1s, but not a default for all Subnet-EVM chains config["warp-api-enabled"] = true; - // State sync configuration - config["state-sync-enabled"] = stateSyncEnabled; + // State sync - only add if enabled + if (stateSyncEnabled) { + config["state-sync-enabled"] = true; + } - // Transaction indexing + // Transaction indexing - only add if non-default if (skipTxIndexing) { config["skip-tx-indexing"] = true; } else if (transactionHistory > 0) { config["transaction-history"] = transactionHistory; } - // Transaction settings + // Transaction settings - only add if enabled if (preimagesEnabled) { config["preimages-enabled"] = true; } @@ -79,6 +139,7 @@ export const generateChainConfig = ( } // Configure APIs based on node type + // Always include eth-apis for explicitness in L1 node setup if (enableDebugTrace) { config["eth-apis"] = [ "eth", @@ -99,7 +160,8 @@ export const generateChainConfig = ( ]; config["admin-api-enabled"] = true; } else { - // Standard APIs (includes internal APIs required for basic eth methods) + // Include standard APIs explicitly for L1 nodes (even though these are defaults) + // This makes the configuration more explicit and easier to understand config["eth-apis"] = [ "eth", "eth-filter", @@ -113,18 +175,24 @@ export const generateChainConfig = ( // RPC-specific settings if (isRPC) { - config["api-max-duration"] = 0; // No time limit - config["api-max-blocks-per-request"] = apiMaxBlocksPerRequest; - config["allow-unfinalized-queries"] = allowUnfinalizedQueries; + // api-max-duration: 0 is default (no time limit), already default so we don't need to add + // api-max-blocks-per-request: 0 is default (no limit) + addIfNotDefault("api-max-blocks-per-request", apiMaxBlocksPerRequest); + + // Only add if enabled (default is false) + if (allowUnfinalizedQueries) { + config["allow-unfinalized-queries"] = true; + } } - // Gossip settings (primarily for validators) + // Gossip settings (primarily for validators) - only add if non-default if (nodeType === 'validator') { + // These don't have documented defaults, so we always add them for validators config["push-gossip-num-validators"] = pushGossipNumValidators; config["push-gossip-percent-stake"] = pushGossipPercentStake; } - // Continuous profiling (if enabled) + // Continuous profiling - only add if enabled if (continuousProfilerDir) { config["continuous-profiler-dir"] = continuousProfilerDir; config["continuous-profiler-frequency"] = continuousProfilerFrequency; From 1377f54dea34b536898a8c43fb5e913c037ea99e Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 18 Nov 2025 14:28:41 -0500 Subject: [PATCH 5/7] nits --- .../console/layer-1/AvalancheGoDockerL1.tsx | 155 ++++++++++++------ .../toolbox/console/layer-1/node-config.ts | 18 +- 2 files changed, 119 insertions(+), 54 deletions(-) diff --git a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx index c9ed95c543a..b7fa57934f6 100644 --- a/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx +++ b/components/toolbox/console/layer-1/AvalancheGoDockerL1.tsx @@ -28,7 +28,9 @@ function AvalanchegoDockerInner() { const [nodeType, setNodeType] = useState<"validator" | "public-rpc">("validator"); const [domain, setDomain] = useState(""); const [enableDebugTrace, setEnableDebugTrace] = useState(false); + const [adminApiEnabled, setAdminApiEnabled] = useState(false); const [pruningEnabled, setPruningEnabled] = useState(true); + const [logLevel, setLogLevel] = useState("info"); const [subnetIdError, setSubnetIdError] = useState(null); const [selectedRPCBlockchainId, setSelectedRPCBlockchainId] = useState(""); const [minDelayTarget, setMinDelayTarget] = useState(250); @@ -90,7 +92,9 @@ function AvalanchegoDockerInner() { const config = generateChainConfig( nodeType, enableDebugTrace, + adminApiEnabled, pruningEnabled, + logLevel, minDelayTarget, trieCleanCache, trieDirtyCache, @@ -120,14 +124,16 @@ function AvalanchegoDockerInner() { } catch (error) { setConfigJson(`Error: ${(error as Error).message}`); } - }, [subnetId, chainId, nodeType, enableDebugTrace, pruningEnabled, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, trieDirtyCommitTarget, triePrefetcherParallelism, snapshotCache, commitInterval, stateSyncServerTrieCache, rpcGasCap, rpcTxFeeCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, batchRequestLimit, batchResponseMaxSize, acceptedCacheSize, transactionHistory, stateSyncEnabled, skipTxIndexing, preimagesEnabled, localTxsEnabled, pushGossipNumValidators, pushGossipPercentStake, continuousProfilerDir, continuousProfilerFrequency]); + }, [subnetId, chainId, nodeType, enableDebugTrace, adminApiEnabled, pruningEnabled, logLevel, blockchainInfo, minDelayTarget, trieCleanCache, trieDirtyCache, trieDirtyCommitTarget, triePrefetcherParallelism, snapshotCache, commitInterval, stateSyncServerTrieCache, rpcGasCap, rpcTxFeeCap, apiMaxBlocksPerRequest, allowUnfinalizedQueries, batchRequestLimit, batchResponseMaxSize, acceptedCacheSize, transactionHistory, stateSyncEnabled, skipTxIndexing, preimagesEnabled, localTxsEnabled, pushGossipNumValidators, pushGossipPercentStake, continuousProfilerDir, continuousProfilerFrequency]); useEffect(() => { if (nodeType === "validator") { // Validator node defaults - optimized for block production setDomain(""); setEnableDebugTrace(false); + setAdminApiEnabled(false); setPruningEnabled(true); + setLogLevel("info"); setMinDelayTarget(250); // Fast block times for L1 setAllowUnfinalizedQueries(false); // Standard cache sizes for validators @@ -138,6 +144,8 @@ function AvalanchegoDockerInner() { setTransactionHistory(0); // Keep all tx history by default } else if (nodeType === "public-rpc") { // RPC node defaults - optimized for query performance + setPruningEnabled(false); // RPC nodes typically need full history + setLogLevel("info"); setAllowUnfinalizedQueries(true); // Enable real-time queries // Larger caches for better RPC performance setTrieCleanCache(1024); // 2x for better read performance @@ -221,7 +229,9 @@ function AvalanchegoDockerInner() { setNodeType("validator"); setDomain(""); setEnableDebugTrace(false); + setAdminApiEnabled(false); setPruningEnabled(true); + setLogLevel("info"); setSubnetIdError(null); setSelectedRPCBlockchainId(""); setMinDelayTarget(250); @@ -312,6 +322,85 @@ function AvalanchegoDockerInner() {
    +
    setHighlightPath('logLevel')} onMouseLeave={clearHighlight}> + + +

    + Controls the verbosity of node logs +

    +
    + {nodeType === "validator" && ( +
    setHighlightPath('minDelayTarget')} onMouseLeave={clearHighlight}> + + { + const value = Math.min(2000, Math.max(0, parseInt(e.target.value) || 0)); + setMinDelayTarget(value); + }} + onFocus={() => setHighlightPath('minDelayTarget')} + onBlur={clearHighlight} + min="0" + max="2000" + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" + /> +

    + The minimum delay between blocks (in milliseconds). Maximum: 2000ms. Default: 250ms. +

    +
    + )} +
    setHighlightPath('pruning')} onMouseLeave={clearHighlight}> + +

    + {nodeType === "validator" + ? "Recommended for validators. Reduces disk usage by removing old state data." + : "Not recommended for RPC nodes that need full historical data."} +

    +
    + +
    setHighlightPath('adminApi')} onMouseLeave={clearHighlight}> + +

    + Enables administrative APIs. Only enable if needed and secured. +

    +
    + {nodeType === "public-rpc" && ( <>
    setHighlightPath('ethApis')} onMouseLeave={clearHighlight}> @@ -324,18 +413,9 @@ function AvalanchegoDockerInner() { /> Enable Debug Trace -
    - -
    setHighlightPath('pruning')} onMouseLeave={clearHighlight}> - +

    + Enables debug APIs and detailed tracing capabilities +

    {subnet && subnet.blockchains && subnet.blockchains.length > 1 && ( @@ -362,30 +442,6 @@ function AvalanchegoDockerInner() { )} - {nodeType === "validator" && ( -
    setHighlightPath('minDelayTarget')} onMouseLeave={clearHighlight}> - - { - const value = Math.min(2000, Math.max(0, parseInt(e.target.value) || 0)); - setMinDelayTarget(value); - }} - onFocus={() => setHighlightPath('minDelayTarget')} - onBlur={clearHighlight} - min="0" - max="2000" - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" - /> -

    - The minimum delay between blocks (in milliseconds). Maximum: 2000ms. Default: 250ms. -

    -
    - )} - {/* Advanced Settings */}
    + + {showAdvancedSettings && ( +
    + + For advanced configuration options, see the{" "} + + AvalancheGo configuration + {" "} + and{" "} + + C-Chain configuration + documentation. + + +
    +

    Cache Settings

    + +
    +
    setHighlightPath('trieCleanCache')} onMouseLeave={clearHighlight}> + + setTrieCleanCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('trieCleanCache')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
    + +
    setHighlightPath('trieDirtyCache')} onMouseLeave={clearHighlight}> + + setTrieDirtyCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('trieDirtyCache')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
    + +
    setHighlightPath('snapshotCache')} onMouseLeave={clearHighlight}> + + setSnapshotCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('snapshotCache')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +
    + +
    setHighlightPath('acceptedCacheSize')} onMouseLeave={clearHighlight}> + + setAcceptedCacheSize(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('acceptedCacheSize')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Depth of accepted headers and logs cache +

    +
    + +
    setHighlightPath('trieDirtyCommitTarget')} onMouseLeave={clearHighlight}> + + setTrieDirtyCommitTarget(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('trieDirtyCommitTarget')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Memory limit before commit +

    +
    + +
    setHighlightPath('triePrefetcherParallelism')} onMouseLeave={clearHighlight}> + + setTriePrefetcherParallelism(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('triePrefetcherParallelism')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max concurrent disk reads +

    +
    + +
    setHighlightPath('stateSyncServerTrieCache')} onMouseLeave={clearHighlight}> + + setStateSyncServerTrieCache(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('stateSyncServerTrieCache')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Trie cache for state sync server +

    +
    +
    +
    + +
    +

    Performance Settings

    + +
    +
    setHighlightPath('commitInterval')} onMouseLeave={clearHighlight}> + + setCommitInterval(Math.max(1, parseInt(e.target.value) || 1))} + onFocus={() => setHighlightPath('commitInterval')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Interval to persist EVM and atomic tries +

    +
    + +
    setHighlightPath('rpcGasCap')} onMouseLeave={clearHighlight}> + + setRpcGasCap(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('rpcGasCap')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Maximum gas limit for RPC calls +

    +
    + +
    setHighlightPath('rpcTxFeeCap')} onMouseLeave={clearHighlight}> + + setRpcTxFeeCap(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('rpcTxFeeCap')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Maximum transaction fee cap +

    +
    +
    +
    + +
    +

    API Limits

    + +
    +
    setHighlightPath('batchRequestLimit')} onMouseLeave={clearHighlight}> + + setBatchRequestLimit(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('batchRequestLimit')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max batched requests (0 = no limit) +

    +
    + +
    setHighlightPath('batchResponseMaxSize')} onMouseLeave={clearHighlight}> + + setBatchResponseMaxSize(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('batchResponseMaxSize')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max batch response size (default: 25MB) +

    +
    +
    +
    + +
    +

    Transaction & State

    + +
    +
    setHighlightPath('transactionHistory')} onMouseLeave={clearHighlight}> + + setTransactionHistory(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('transactionHistory')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Max blocks to keep tx indices. 0 = archive mode (all history) +

    +
    + +
    setHighlightPath('skipTxIndexing')} onMouseLeave={clearHighlight}> + +

    + Disable tx indexing entirely (saves disk space) +

    +
    + +
    setHighlightPath('stateSyncEnabled')} onMouseLeave={clearHighlight}> + +

    + Fast sync from state summary +

    +
    + +
    setHighlightPath('preimagesEnabled')} onMouseLeave={clearHighlight}> + +

    + Record preimages (uses more disk) +

    +
    + +
    setHighlightPath('localTxsEnabled')} onMouseLeave={clearHighlight}> + +

    + Treat local account txs as local +

    +
    +
    +
    + + {nodeType === "validator" && ( +
    +

    Gossip Settings (Validator)

    + +
    +
    setHighlightPath('pushGossipNumValidators')} onMouseLeave={clearHighlight}> + + setPushGossipNumValidators(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('pushGossipNumValidators')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Number of validators to push gossip to +

    +
    + +
    setHighlightPath('pushGossipPercentStake')} onMouseLeave={clearHighlight}> + + setPushGossipPercentStake(Math.min(1, Math.max(0, parseFloat(e.target.value) || 0)))} + onFocus={() => setHighlightPath('pushGossipPercentStake')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Percentage of total stake to gossip to (0-1) +

    +
    +
    +
    + )} + + {isRPC && ( +
    +

    RPC-Specific Settings

    + +
    +
    setHighlightPath('apiMaxBlocksPerRequest')} onMouseLeave={clearHighlight}> + + setApiMaxBlocksPerRequest(Math.max(0, parseInt(e.target.value) || 0))} + onFocus={() => setHighlightPath('apiMaxBlocksPerRequest')} + onBlur={clearHighlight} + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + 0 = no limit. Limits blocks per getLogs request +

    +
    + +
    setHighlightPath('allowUnfinalizedQueries')} onMouseLeave={clearHighlight}> + +

    + Allows queries for unfinalized/pending blocks +

    +
    +
    +
    + )} + +
    +

    Profiling (Optional)

    + +
    +
    setHighlightPath('continuousProfilerDir')} onMouseLeave={clearHighlight}> + + setContinuousProfilerDir(e.target.value)} + onFocus={() => setHighlightPath('continuousProfilerDir')} + onBlur={clearHighlight} + placeholder="./profiles (leave empty to disable)" + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + Directory for continuous profiler output +

    +
    + + {continuousProfilerDir && ( +
    setHighlightPath('continuousProfilerFrequency')} onMouseLeave={clearHighlight}> + + setContinuousProfilerFrequency(e.target.value)} + onFocus={() => setHighlightPath('continuousProfilerFrequency')} + onBlur={clearHighlight} + placeholder="15m" + className="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md dark:bg-gray-700 dark:text-white" + /> +

    + How often to create profiles (e.g., 15m, 1h) +

    +
    + )} +
    +
    +
    + )} +
    +
    + + {/* Configuration Preview */} +
    +
    +
    +

    Configuration Preview

    +
    +
    + {configJson && !configJson.startsWith("Error:") ? ( + + ) : ( +
    + {configJson.startsWith("Error:") ? configJson : "Configure your node to see the chain config"}
    )}
    - )} - +
    +
    +
    -

    Start AvalancheGo Node

    -

    Run the following Docker command to start your Primary Network node:

    +

    Run Docker Command

    +

    + Run the following Docker command to start your Primary Network node: +

    - + - + -

    To run multiple validator nodes on the same machine, ensure each node has:

    -
      +

      To run multiple nodes on the same machine, ensure each node has:

      +
      • Unique container name (change --name parameter)
      • -
      • Different ports (modify -p parameters)
      • -
      • Separate data directories (change the local volume path ~/.avalanchego to a unique directory)
      • +
      • Different ports (modify port mappings)
      • +
      • Separate data directories (change ~/.avalanchego path)
      -

      Example for second node: Use ports 9652/9653, container name "avago2", and data directory "~/.avalanchego2"

      +

      Example for second node: Use ports 9652/9653, container name "avago2", and data directory "~/.avalanchego2"

      + + + +

      Monitor your node with:

      +
      - {/* Conditional steps based on node type */} {nodeType === "public-rpc" && ( )} - - {nodeType === "validator" && ( -

      Wait for the Node to Bootstrap

      +

      Wait for the Node to Bootstrap

      Your node will now bootstrap and sync the Primary Network (P-Chain, X-Chain, and C-Chain). This process can take several hours to days depending on your hardware and network connection.

      You can follow the process by checking the logs with the following command:

      @@ -219,7 +929,6 @@ export default function AvalancheGoDockerPrimaryNetwork() {
      )} - {/* Show success message when node is ready for validator mode */} {nodeIsReady && nodeType === "validator" && (

      Node Setup Complete

      @@ -246,8 +955,21 @@ export default function AvalancheGoDockerPrimaryNetwork() { )} - + {configJson && !configJson.startsWith("Error:") && ( +
      + +
      + )} - + ); +} + +export default function AvalancheGoDockerPrimaryNetwork() { + return ( + + + ); } diff --git a/components/toolbox/stores/createChainStore.ts b/components/toolbox/stores/createChainStore.ts index 128cb32900a..c98ce36d91d 100644 --- a/components/toolbox/stores/createChainStore.ts +++ b/components/toolbox/stores/createChainStore.ts @@ -9,7 +9,7 @@ const createChainInitialState = { chainName: "", managerAddress: "0xfacade0000000000000000000000000000000000", genesisData: "", - targetBlockRate: 2, + targetBlockRate: 1, gasLimit: 12000000, evmChainId: Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000, convertToL1TxId: "", From 3df4ba72aabd7278c00a589b0a16d8c546431cd2 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 25 Nov 2025 15:41:58 -0600 Subject: [PATCH 7/7] dont use AVAGO_CHAIN_CONFIG_CONTENT for primary network --- .../AvalancheGoDockerPrimaryNetwork.tsx | 96 ++++++++++++++++--- 1 file changed, 81 insertions(+), 15 deletions(-) diff --git a/components/toolbox/console/primary-network/AvalancheGoDockerPrimaryNetwork.tsx b/components/toolbox/console/primary-network/AvalancheGoDockerPrimaryNetwork.tsx index 5afbbf8c63c..5fbda2b8d42 100644 --- a/components/toolbox/console/primary-network/AvalancheGoDockerPrimaryNetwork.tsx +++ b/components/toolbox/console/primary-network/AvalancheGoDockerPrimaryNetwork.tsx @@ -12,9 +12,10 @@ import { ReverseProxySetup } from "@/components/toolbox/components/ReverseProxyS import { Button } from "@/components/toolbox/components/Button"; import { SyntaxHighlightedJSON } from "@/components/toolbox/components/genesis/SyntaxHighlightedJSON"; import { GenesisHighlightProvider, useGenesisHighlight } from "@/components/toolbox/components/genesis/GenesisHighlightContext"; -import { generateChainConfig } from "@/components/toolbox/console/layer-1/node-config"; +import { generateChainConfig, generateConfigFileCommand } from "@/components/toolbox/console/layer-1/node-config"; import { useNodeConfigHighlighting } from "@/components/toolbox/console/layer-1/useNodeConfigHighlighting"; -import { C_CHAIN_ID, generateDockerCommand } from "@/components/toolbox/console/layer-1/create/config"; +import { C_CHAIN_ID } from "@/components/toolbox/console/layer-1/create/config"; +import { getContainerVersions } from "@/components/toolbox/utils/containerVersions"; function AvalancheGoDockerPrimaryNetworkInner() { const { setHighlightPath, clearHighlight, highlightPath } = useGenesisHighlight(); @@ -183,25 +184,53 @@ function AvalancheGoDockerPrimaryNetworkInner() { setNodeIsReady(false); }; - // Generate Docker command for Primary Network + // Generate Docker command for Primary Network (using file-based config) const getDockerCommand = () => { try { - return generateDockerCommand( - [], // No subnets for Primary Network - isRPC, - avalancheNetworkID, - C_CHAIN_ID, - "", // No custom VM ID for Primary Network - enableDebugTrace, - pruningEnabled, - true, // isPrimaryNetwork = true - nodeType === "validator" ? minDelayTarget : null - ); + const isTestnet = avalancheNetworkID === 5; + const versions = getContainerVersions(isTestnet); + + const env: Record = { + AVAGO_PUBLIC_IP_RESOLUTION_SERVICE: "opendns", + AVAGO_HTTP_HOST: "0.0.0.0", + AVAGO_CHAIN_CONFIG_DIR: "/root/.avalanchego/configs/chains" + }; + + // Set network ID + if (avalancheNetworkID === 5) { + env.AVAGO_NETWORK_ID = "fuji"; + } + + // Configure RPC settings + if (isRPC) { + env.AVAGO_HTTP_ALLOWED_HOSTS = '"*"'; + } + + const chunks = [ + "docker run -it -d", + "--name avago", + `-p ${isRPC ? "" : "127.0.0.1:"}9650:9650 -p 9651:9651`, + "-v ~/.avalanchego:/root/.avalanchego", + ...Object.entries(env).map(([key, value]) => `-e ${key}=${value}`), + `avaplatform/avalanchego:${versions['avaplatform/avalanchego']}` + ]; + + return chunks.map(chunk => ` ${chunk}`).join(" \\\n").trim(); } catch (error) { return `# Error: ${(error as Error).message}`; } }; + // Generate the config file command + const getConfigFileCommand = () => { + try { + const config = JSON.parse(configJson); + return generateConfigFileCommand(C_CHAIN_ID, config); + } catch { + return "# Error generating config file command"; + } + }; + return (
      + +

      Create Configuration File

      +

      + Run this command on your server to create the C-Chain configuration file: +

      + + + +

      + This creates the configuration file at ~/.avalanchego/configs/chains/{C_CHAIN_ID}/config.json +

      +

      + Read the documentation for more information on the configuration options. {" "} + + AvalancheGo configuration + + {" "}and{" "} + + C-Chain configuration + +

      +
      +

      Run Docker Command

      - Run the following Docker command to start your Primary Network node: + Start the node using Docker:

      +

      + The container will read the config from ~/.avalanchego/configs/chains/{C_CHAIN_ID}/config.json via the mounted volume. +

      +

      To run multiple nodes on the same machine, ensure each node has: