diff --git a/clients/js/packages/client/__tests__/client.verify.test.ts b/clients/js/packages/client/__tests__/client.verify.test.ts
index 0110c7171..e925fedc0 100644
--- a/clients/js/packages/client/__tests__/client.verify.test.ts
+++ b/clients/js/packages/client/__tests__/client.verify.test.ts
@@ -221,4 +221,124 @@ describe('StarshipClient verify', () => {
await client.verify();
expectClient(ctx, 0);
});
+
+ it('should handle ingress verification', async () => {
+ const { client, ctx } = createClient();
+ client.dependencies.forEach((dep) => (dep.installed = true));
+
+ const configWithIngress = {
+ ...config.config,
+ ingress: {
+ enabled: true,
+ host: 'test.example.com',
+ type: 'nginx'
+ }
+ };
+
+ client.setConfig(configWithIngress);
+
+ // Mock ingress endpoints
+ mockedAxios.get.mockImplementation((url) => {
+ // Chain REST endpoints
+ if (url.includes('rest.osmosis-1-genesis.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: { supply: [] }
+ });
+ }
+ if (url.includes('rest.cosmos-2-genesis.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: { supply: [] }
+ });
+ }
+
+ // Chain RPC endpoints
+ if (url.includes('rpc.osmosis-1-genesis.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: { result: { node_info: {} } }
+ });
+ }
+ if (url.includes('rpc.cosmos-2-genesis.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: { result: { node_info: {} } }
+ });
+ }
+
+ // Registry endpoint
+ if (url.includes('registry.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: { chains: [] }
+ });
+ }
+
+ // Explorer endpoint
+ if (url.includes('explorer.test.example.com')) {
+ return Promise.resolve({
+ status: 200,
+ data: '
Ping Dashboard'
+ });
+ }
+
+ // Throw error for unhandled URLs
+ throw new Error(`Unhandled URL in mock: ${url}`);
+ });
+
+ await client.verify();
+ expectClient(ctx, 0);
+ });
+
+ it('should handle ingress verification failure', async () => {
+ const { client, ctx } = createClient();
+ client.dependencies.forEach((dep) => (dep.installed = true));
+
+ const configWithIngress = {
+ ...config.config,
+ ingress: {
+ enabled: true,
+ host: 'test.example.com',
+ type: 'nginx'
+ }
+ };
+
+ client.setConfig(configWithIngress);
+
+ // Mock ingress endpoints with failures
+ mockedAxios.get.mockImplementation((url) => {
+ // Chain REST endpoints
+ if (url.includes('rest.osmosis-1-genesis.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+ if (url.includes('rest.cosmos-2-genesis.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+
+ // Chain RPC endpoints
+ if (url.includes('rpc.osmosis-1-genesis.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+ if (url.includes('rpc.cosmos-2-genesis.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+
+ // Registry endpoint
+ if (url.includes('registry.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+
+ // Explorer endpoint
+ if (url.includes('explorer.test.example.com')) {
+ return Promise.reject(new Error('Connection refused'));
+ }
+
+ // Throw error for unhandled URLs
+ throw new Error(`Unhandled URL in mock: ${url}`);
+ });
+
+ await client.verify();
+ expectClient(ctx, 1);
+ });
});
diff --git a/clients/js/packages/client/src/config.ts b/clients/js/packages/client/src/config.ts
index 5287cd7ca..f8975a8de 100644
--- a/clients/js/packages/client/src/config.ts
+++ b/clients/js/packages/client/src/config.ts
@@ -9,6 +9,8 @@ export interface Ports {
grafana?: number;
cometmock?: number;
ws?: number;
+ http?: number;
+ [key: string]: number | undefined;
}
export interface Resources {
@@ -134,12 +136,11 @@ export interface Monitoring {
export interface Ingress {
enabled: boolean;
+ host: string;
type: string;
- host?: string;
certManager?: {
- issuer?: string;
+ issuer: string;
};
- resources?: Resources;
}
export interface Images {
diff --git a/clients/js/packages/client/src/verifiers/chain.ts b/clients/js/packages/client/src/verifiers/chain.ts
index daf2cce13..2e35b3047 100644
--- a/clients/js/packages/client/src/verifiers/chain.ts
+++ b/clients/js/packages/client/src/verifiers/chain.ts
@@ -1,19 +1,24 @@
import axios from 'axios';
-import { Chain } from '../config';
-import { handleAxiosError } from '../utils';
-import { ChainVerifierSet, VerificationResult } from './types';
+import { Chain, StarshipConfig } from '../config';
+import {
+ ChainVerifierSet,
+ handleAxiosError,
+ VerificationResult
+} from './types';
+import { getServiceUrl } from './utils';
export const verifyChainRest = async (
- chain: Chain
+ chain: Chain,
+ config: StarshipConfig
): Promise => {
- const port = chain.ports?.rest;
const result: VerificationResult = {
service: `chain-${chain.id}`,
endpoint: 'rest',
status: 'failure'
};
+ const port = chain.ports?.rest;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -21,40 +26,53 @@ export const verifyChainRest = async (
}
try {
- const response = await axios.get(
- `http://localhost:${port}/cosmos/bank/v1beta1/supply`
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'chain',
+ 'rest',
+ String(chain.id)
);
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
result.error = 'Failed to get chain supply';
return result;
}
- if (response.data.supply[0].amount > 0) {
+
+ if (response.data.supply) {
result.status = 'success';
- result.message = 'Chain supply is greater than 0';
+ result.message = 'Chain REST is working';
return result;
}
result.status = 'failure';
- result.error = 'Chain supply not confirmed';
- result.details = response.data;
+ result.error = 'Invalid supply response';
return result;
} catch (error) {
- result.error = handleAxiosError(error);
+ if (axios.isAxiosError(error)) {
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Chain REST service is not running';
+ } else {
+ result.error = handleAxiosError(error);
+ }
+ } else {
+ result.error = 'Unknown error occurred';
+ }
return result;
}
};
export const verifyChainRpc = async (
- chain: Chain
+ chain: Chain,
+ config: StarshipConfig
): Promise => {
- const port = chain.ports?.rpc;
const result: VerificationResult = {
service: `chain-${chain.id}`,
endpoint: 'rpc',
status: 'failure'
};
+ const port = chain.ports?.rpc;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -62,44 +80,53 @@ export const verifyChainRpc = async (
}
try {
- const response = await axios.get(`http://localhost:${port}/status`);
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'chain',
+ 'rpc',
+ String(chain.id)
+ );
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
- result.error = 'Failed to get chain node info';
+ result.error = 'Failed to get chain status';
return result;
}
- const blockHeight = Number(
- response.data.result?.sync_info?.latest_block_height ||
- response.data.result?.SyncInfo?.latest_block_height
- );
-
- if (blockHeight > 0) {
+ if (response.data.result?.sync_info) {
result.status = 'success';
- result.message = 'Chain is synced';
+ result.message = 'Chain RPC is working';
return result;
}
result.status = 'failure';
- result.error = 'Block height is 0';
- result.details = response.data;
+ result.error = 'Invalid status response';
return result;
} catch (error) {
- result.error = handleAxiosError(error);
+ if (axios.isAxiosError(error)) {
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Chain RPC service is not running';
+ } else {
+ result.error = handleAxiosError(error);
+ }
+ } else {
+ result.error = 'Unknown error occurred';
+ }
return result;
}
};
export const verifyChainFaucet = async (
- chain: Chain
+ chain: Chain,
+ config: StarshipConfig
): Promise => {
- const port = chain.ports?.faucet;
const result: VerificationResult = {
service: `chain-${chain.id}`,
endpoint: 'faucet',
status: 'failure'
};
+ const port = chain.ports?.faucet;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -107,44 +134,53 @@ export const verifyChainFaucet = async (
}
try {
- const response = await axios.get(`http://localhost:${port}/status`);
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'chain',
+ 'faucet',
+ String(chain.id)
+ );
+ const response = await axios.get(`${baseUrl}${path}`);
+ result.details = response.data;
if (response.status !== 200) {
- result.error = 'Failed to get chain node info';
- return result;
- }
-
- if (!response.data.chainId) {
- result.error = 'Invalid response: chainId not found';
- result.details = response.data;
+ result.error = 'Failed to get faucet status';
return result;
}
- if (response.data.chainId === chain.id) {
+ if (response.data.chainId) {
result.status = 'success';
result.message = 'Chain faucet is working';
return result;
}
result.status = 'failure';
- result.error = `Chain ID mismatch: expected ${chain.id}, got ${response.data.chainId}`;
- result.details = response.data;
+ result.error = 'Invalid faucet response';
return result;
} catch (error) {
- result.error = handleAxiosError(error);
+ if (axios.isAxiosError(error)) {
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Faucet service is not running';
+ } else {
+ result.error = handleAxiosError(error);
+ }
+ } else {
+ result.error = 'Unknown error occurred';
+ }
return result;
}
};
export const verifyChainExposer = async (
- chain: Chain
+ chain: Chain,
+ config: StarshipConfig
): Promise => {
- const port = chain.ports?.exposer;
const result: VerificationResult = {
service: `chain-${chain.id}`,
endpoint: 'exposer',
status: 'failure'
};
+ const port = chain.ports?.exposer;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -152,7 +188,13 @@ export const verifyChainExposer = async (
}
try {
- const response = await axios.get(`http://localhost:${port}/node_id`);
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'chain',
+ 'exposer',
+ String(chain.id)
+ );
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
result.error = 'Failed to get chain node id';
diff --git a/clients/js/packages/client/src/verifiers/explorer.ts b/clients/js/packages/client/src/verifiers/explorer.ts
index 838547a63..5ab1dd9b5 100644
--- a/clients/js/packages/client/src/verifiers/explorer.ts
+++ b/clients/js/packages/client/src/verifiers/explorer.ts
@@ -1,19 +1,20 @@
import axios from 'axios';
-import { Explorer } from '../config';
-import { handleAxiosError } from '../utils';
-import { VerificationResult } from './types';
+import { Explorer, StarshipConfig } from '../config';
+import { handleAxiosError, VerificationResult } from './types';
+import { getServiceUrl } from './utils';
export const verifyExplorerRest = async (
- explorer: Explorer
+ explorer: Explorer,
+ config: StarshipConfig
): Promise => {
- const port = explorer.ports?.rest;
const result: VerificationResult = {
service: 'explorer',
- endpoint: 'rest',
+ endpoint: 'http',
status: 'failure'
};
+ const port = explorer.ports?.http;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -21,13 +22,9 @@ export const verifyExplorerRest = async (
}
try {
- const response = await axios.get(`http://localhost:${port}`, {
- headers: {
- Accept: 'text/html'
- }
- });
+ const { baseUrl, path } = getServiceUrl(config, 'explorer', 'http');
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
-
if (response.status !== 200) {
result.error = 'Failed to get explorer status';
return result;
@@ -40,10 +37,18 @@ export const verifyExplorerRest = async (
}
result.status = 'failure';
- result.error = 'Explorer is not working';
+ result.error = 'Invalid explorer response';
return result;
} catch (error) {
- result.error = handleAxiosError(error);
+ if (axios.isAxiosError(error)) {
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Explorer service is not running';
+ } else {
+ result.error = handleAxiosError(error);
+ }
+ } else {
+ result.error = 'Unknown error occurred';
+ }
return result;
}
};
diff --git a/clients/js/packages/client/src/verifiers/index.ts b/clients/js/packages/client/src/verifiers/index.ts
index e81b1a8a9..2773105d2 100644
--- a/clients/js/packages/client/src/verifiers/index.ts
+++ b/clients/js/packages/client/src/verifiers/index.ts
@@ -1,5 +1,6 @@
import { chainVerifiers } from './chain';
import { verifyExplorerRest } from './explorer';
+import { verifyIngress } from './ingress';
import { verifyRegistryRest } from './registry';
import { relayerVerifiers } from './relayer';
import { VerificationFunction, VerificationResult } from './types';
@@ -14,7 +15,7 @@ export const verifyChains: VerificationFunction = async (config) => {
for (const chain of config.chains) {
const verifierSet = chainVerifiers[chain.name] || chainVerifiers.default;
for (const [, verifier] of Object.entries(verifierSet)) {
- const result = await verifier(chain);
+ const result = await verifier(chain, config);
results.push(result);
}
}
@@ -31,7 +32,7 @@ export const verifyRelayers: VerificationFunction = async (config) => {
for (const relayer of config.relayers) {
for (const [, verifier] of Object.entries(relayerVerifiers)) {
- const result = await verifier(relayer);
+ const result = await verifier(relayer, config);
results.push(result);
}
}
@@ -47,7 +48,7 @@ export const verifyRegistry: VerificationFunction = async (config) => {
}
const registryResults = await Promise.all([
- verifyRegistryRest(config.registry)
+ verifyRegistryRest(config.registry, config)
]);
results.push(...registryResults);
@@ -61,7 +62,7 @@ export const verifyExplorer: VerificationFunction = async (config) => {
return results;
}
- const explorerResult = await verifyExplorerRest(config.explorer);
+ const explorerResult = await verifyExplorerRest(config.explorer, config);
results.push(explorerResult);
return results;
};
@@ -71,12 +72,14 @@ export const verify: VerificationFunction = async (config) => {
const relayerResults = await verifyRelayers(config);
const registryResults = await verifyRegistry(config);
const explorerResults = await verifyExplorer(config);
+ const ingressResults = await verifyIngress(config);
return [
...chainResults,
...relayerResults,
...registryResults,
- ...explorerResults
+ ...explorerResults,
+ ...ingressResults
];
};
diff --git a/clients/js/packages/client/src/verifiers/ingress.ts b/clients/js/packages/client/src/verifiers/ingress.ts
new file mode 100644
index 000000000..40b66bfd2
--- /dev/null
+++ b/clients/js/packages/client/src/verifiers/ingress.ts
@@ -0,0 +1,154 @@
+import axios from 'axios';
+
+import { Chain, Relayer, StarshipConfig } from '../config';
+import { handleAxiosError, VerificationResult } from './types';
+import { getServiceUrl } from './utils';
+
+const verifyIngressEndpoint = async (
+ url: string,
+ service: string,
+ endpoint: string
+): Promise => {
+ const result: VerificationResult = {
+ service,
+ endpoint,
+ status: 'failure'
+ };
+
+ try {
+ const response = await axios.get(url);
+ if (response.status === 200) {
+ result.status = 'success';
+ result.message = `${endpoint} endpoint is accessible`;
+ return result;
+ }
+
+ result.error = `Failed to access ${endpoint} endpoint`;
+ return result;
+ } catch (error) {
+ result.error = handleAxiosError(error);
+ return result;
+ }
+};
+
+export const verifyChainIngress = async (
+ chain: Chain,
+ config: StarshipConfig
+): Promise => {
+ const results: VerificationResult[] = [];
+
+ // Verify REST endpoint
+ if (chain.ports?.rest) {
+ const { baseUrl, path } = getServiceUrl(config, 'chain', 'rest', String(chain.id));
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `chain-${chain.id}`, 'rest')
+ );
+ }
+
+ // Verify RPC endpoint
+ if (chain.ports?.rpc) {
+ const { baseUrl, path } = getServiceUrl(config, 'chain', 'rpc', String(chain.id));
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `chain-${chain.id}`, 'rpc')
+ );
+ }
+
+ // Verify Faucet endpoint
+ if (chain.ports?.faucet) {
+ const { baseUrl, path } = getServiceUrl(config, 'chain', 'faucet', String(chain.id));
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `chain-${chain.id}`, 'faucet')
+ );
+ }
+
+ // Verify Exposer endpoint
+ if (chain.ports?.exposer) {
+ const { baseUrl, path } = getServiceUrl(config, 'chain', 'exposer', String(chain.id));
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `chain-${chain.id}`, 'exposer')
+ );
+ }
+
+ return results;
+};
+
+export const verifyRelayerIngress = async (
+ relayer: Relayer,
+ config: StarshipConfig
+): Promise => {
+ const results: VerificationResult[] = [];
+
+ if (relayer.type === 'hermes') {
+ // Verify REST endpoint
+ if (relayer.ports?.rest) {
+ const { baseUrl, path } = getServiceUrl(config, 'relayer', 'rest', relayer.chains[0]);
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `relayer-${relayer.name}`, 'rest')
+ );
+ }
+
+ // Verify Exposer endpoint
+ if (relayer.ports?.exposer) {
+ const { baseUrl, path } = getServiceUrl(config, 'relayer', 'exposer', relayer.chains[0]);
+ results.push(
+ await verifyIngressEndpoint(`${baseUrl}${path}`, `relayer-${relayer.name}`, 'exposer')
+ );
+ }
+ }
+
+ return results;
+};
+
+export const verifyRegistryIngress = async (
+ config: StarshipConfig
+): Promise => {
+ const results: VerificationResult[] = [];
+
+ if (config.registry?.enabled) {
+ const { baseUrl, path } = getServiceUrl(config, 'registry', 'rest');
+ results.push(await verifyIngressEndpoint(`${baseUrl}${path}`, 'registry', 'rest'));
+ }
+
+ return results;
+};
+
+export const verifyExplorerIngress = async (
+ config: StarshipConfig
+): Promise => {
+ const results: VerificationResult[] = [];
+
+ if (config.explorer?.enabled) {
+ const { baseUrl, path } = getServiceUrl(config, 'explorer', 'http');
+ results.push(await verifyIngressEndpoint(`${baseUrl}${path}`, 'explorer', 'http'));
+ }
+
+ return results;
+};
+
+export const verifyIngress = async (
+ config: StarshipConfig
+): Promise => {
+ const results: VerificationResult[] = [];
+
+ if (!config.ingress?.enabled || !config.ingress?.host) {
+ return results;
+ }
+
+ // Verify chain ingress endpoints
+ for (const chain of config.chains) {
+ results.push(...(await verifyChainIngress(chain, config)));
+ }
+
+ // Verify relayer ingress endpoints
+ for (const relayer of config.relayers || []) {
+ results.push(...(await verifyRelayerIngress(relayer, config)));
+ }
+
+ // Verify registry ingress endpoint
+ results.push(...(await verifyRegistryIngress(config)));
+
+ // Verify explorer ingress endpoint
+ results.push(...(await verifyExplorerIngress(config)));
+
+ return results;
+};
diff --git a/clients/js/packages/client/src/verifiers/registry.ts b/clients/js/packages/client/src/verifiers/registry.ts
index 66f7e1a9f..0d71979d1 100644
--- a/clients/js/packages/client/src/verifiers/registry.ts
+++ b/clients/js/packages/client/src/verifiers/registry.ts
@@ -1,19 +1,20 @@
import axios from 'axios';
-import { Registry } from '../config';
-import { handleAxiosError } from '../utils';
-import { VerificationResult } from './types';
+import { Registry, StarshipConfig } from '../config';
+import { handleAxiosError, VerificationResult } from './types';
+import { getServiceUrl } from './utils';
export const verifyRegistryRest = async (
- registry: Registry
+ registry: Registry,
+ config: StarshipConfig
): Promise => {
- const port = registry.ports?.rest;
const result: VerificationResult = {
service: 'registry',
endpoint: 'rest',
status: 'failure'
};
+ const port = registry.ports?.rest;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -21,7 +22,8 @@ export const verifyRegistryRest = async (
}
try {
- const response = await axios.get(`http://localhost:${port}/chains`);
+ const { baseUrl, path } = getServiceUrl(config, 'registry', 'rest');
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
result.error = 'Failed to get registry chains';
@@ -38,7 +40,15 @@ export const verifyRegistryRest = async (
result.error = 'Registry is not working';
return result;
} catch (error) {
- result.error = handleAxiosError(error);
+ if (axios.isAxiosError(error)) {
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Registry service is not running';
+ } else {
+ result.error = handleAxiosError(error);
+ }
+ } else {
+ result.error = 'Unknown error occurred';
+ }
return result;
}
};
diff --git a/clients/js/packages/client/src/verifiers/relayer.ts b/clients/js/packages/client/src/verifiers/relayer.ts
index eaabcca11..8c06e5aff 100644
--- a/clients/js/packages/client/src/verifiers/relayer.ts
+++ b/clients/js/packages/client/src/verifiers/relayer.ts
@@ -1,19 +1,24 @@
import axios from 'axios';
-import { Relayer } from '../config';
-import { handleAxiosError } from '../utils';
-import { RelayerVerifierSet, VerificationResult } from './types';
+import { Relayer, StarshipConfig } from '../config';
+import {
+ handleAxiosError,
+ RelayerVerifierSet,
+ VerificationResult
+} from './types';
+import { getServiceUrl } from './utils';
export const verifyRelayerRest = async (
- relayer: Relayer
+ relayer: Relayer,
+ config: StarshipConfig
): Promise => {
- const port = relayer.ports?.rest;
const result: VerificationResult = {
service: `relayer-${relayer.name}`,
endpoint: 'rest',
status: 'failure'
};
+ const port = relayer.ports?.rest;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
@@ -21,28 +26,32 @@ export const verifyRelayerRest = async (
}
try {
- const response = await axios.get(`http://localhost:${port}/status`);
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'relayer',
+ 'rest',
+ relayer.chains[0]
+ );
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
result.error = 'Failed to get relayer status';
return result;
}
- if (response.data.connections && response.data.connections.length > 0) {
+ if (response.data.status === 'ok') {
result.status = 'success';
- result.message = 'Relayer has active connections';
+ result.message = 'Relayer REST is working';
return result;
}
result.status = 'failure';
- result.error = 'No active connections found';
+ result.error = 'Invalid relayer status';
return result;
} catch (error) {
if (axios.isAxiosError(error)) {
- if (error.response?.status === 404) {
- result.error = 'Relayer endpoint not found';
- } else if (error.code === 'ECONNREFUSED') {
- result.error = 'Relayer service is not running';
+ if (error.code === 'ECONNREFUSED') {
+ result.error = 'Relayer REST service is not running';
} else {
result.error = handleAxiosError(error);
}
@@ -54,23 +63,35 @@ export const verifyRelayerRest = async (
};
export const verifyRelayerExposer = async (
- relayer: Relayer
+ relayer: Relayer,
+ config: StarshipConfig
): Promise => {
- const port = relayer.ports?.exposer;
const result: VerificationResult = {
service: `relayer-${relayer.name}`,
endpoint: 'exposer',
status: 'failure'
};
+ const port = relayer.ports?.exposer;
if (!port) {
result.status = 'skipped';
result.error = 'Port not found';
return result;
}
+ if (!relayer.chains || relayer.chains.length === 0) {
+ result.status = 'skipped';
+ result.error = 'No chains configured for relayer';
+ return result;
+ }
try {
- const response = await axios.get(`http://localhost:${port}/config`);
+ const { baseUrl, path } = getServiceUrl(
+ config,
+ 'relayer',
+ 'exposer',
+ relayer.chains[0]
+ );
+ const response = await axios.get(`${baseUrl}${path}`);
result.details = response.data;
if (response.status !== 200) {
result.error = 'Failed to get relayer config';
@@ -88,11 +109,7 @@ export const verifyRelayerExposer = async (
return result;
} catch (error) {
if (axios.isAxiosError(error)) {
- if (error.response?.status === 404) {
- result.error = 'Relayer exposer endpoint not found';
- } else if (error.response?.status === 500) {
- result.error = 'Relayer exposer service error';
- } else if (error.code === 'ECONNREFUSED') {
+ if (error.code === 'ECONNREFUSED') {
result.error = 'Relayer exposer service is not running';
} else {
result.error = handleAxiosError(error);
diff --git a/clients/js/packages/client/src/verifiers/types.ts b/clients/js/packages/client/src/verifiers/types.ts
index 2ff530747..4dd8a630e 100644
--- a/clients/js/packages/client/src/verifiers/types.ts
+++ b/clients/js/packages/client/src/verifiers/types.ts
@@ -14,9 +14,25 @@ export type VerificationFunction = (
) => Promise;
export type ChainVerifierSet = {
- [K in keyof Ports]?: (chain: Chain) => Promise;
+ [K in keyof Ports]?: (
+ chain: Chain,
+ config: StarshipConfig
+ ) => Promise;
};
export type RelayerVerifierSet = {
- [K in keyof Ports]?: (relayer: Relayer) => Promise;
+ [K in keyof Ports]?: (
+ relayer: Relayer,
+ config: StarshipConfig
+ ) => Promise;
+};
+
+export const handleAxiosError = (error: any): string => {
+ if (error.response) {
+ return `HTTP ${error.response.status}: ${error.response.data?.message || error.message}`;
+ }
+ if (error.request) {
+ return `No response received: ${error.message}`;
+ }
+ return error.message || 'Unknown error occurred';
};
diff --git a/clients/js/packages/client/src/verifiers/utils.ts b/clients/js/packages/client/src/verifiers/utils.ts
new file mode 100644
index 000000000..8350cc72d
--- /dev/null
+++ b/clients/js/packages/client/src/verifiers/utils.ts
@@ -0,0 +1,144 @@
+import { StarshipConfig } from '../config';
+
+export interface ServiceUrl {
+ baseUrl: string;
+ path: string;
+}
+
+export const getServiceUrl = (
+ config: StarshipConfig,
+ service: string,
+ endpoint: string,
+ chainId?: string
+): ServiceUrl => {
+ const useIngress = config.ingress?.enabled && config.ingress?.host;
+ let host = 'localhost';
+ if (useIngress && config.ingress) {
+ host = config.ingress.host.replace('*.', '');
+ }
+
+ switch (service) {
+ case 'chain': {
+ if (!chainId) throw new Error('Chain ID is required for chain service');
+ const chain = config.chains.find((c) => c.id === chainId);
+ if (!chain) throw new Error(`Chain ${chainId} not found`);
+
+ const port = chain.ports?.[endpoint];
+ if (!port) throw new Error(`Port not found for ${endpoint}`);
+
+ if (useIngress) {
+ switch (endpoint) {
+ case 'rest':
+ return {
+ baseUrl: `https://rest.${chainId}-genesis.${host}`,
+ path: '/cosmos/bank/v1beta1/supply'
+ };
+ case 'rpc':
+ return {
+ baseUrl: `https://rpc.${chainId}-genesis.${host}`,
+ path: '/status'
+ };
+ case 'faucet':
+ return {
+ baseUrl: `https://rest.${chainId}-genesis.${host}`,
+ path: '/faucet/status'
+ };
+ case 'exposer':
+ return {
+ baseUrl: `https://rest.${chainId}-genesis.${host}`,
+ path: '/exposer/node_id'
+ };
+ default:
+ throw new Error(`Unknown endpoint ${endpoint} for chain service`);
+ }
+ }
+
+ return {
+ baseUrl: `http://localhost:${port}`,
+ path:
+ endpoint === 'rest'
+ ? '/cosmos/bank/v1beta1/supply'
+ : endpoint === 'rpc'
+ ? '/status'
+ : endpoint === 'faucet'
+ ? '/status'
+ : endpoint === 'exposer'
+ ? '/node_id'
+ : ''
+ };
+ }
+
+ case 'relayer': {
+ if (!chainId) throw new Error('Chain ID is required for relayer service');
+ const relayer = config.relayers?.find((r) => r.chains.includes(chainId));
+ if (!relayer) throw new Error(`Relayer for chain ${chainId} not found`);
+
+ const port = relayer.ports?.[endpoint];
+ if (!port) throw new Error(`Port not found for ${endpoint}`);
+
+ if (useIngress) {
+ switch (endpoint) {
+ case 'rest':
+ return {
+ baseUrl: `https://rest.${relayer.type}-${relayer.name}.${host}`,
+ path: '/status'
+ };
+ case 'exposer':
+ return {
+ baseUrl: `https://rest.${relayer.type}-${relayer.name}.${host}`,
+ path: '/exposer/config'
+ };
+ default:
+ throw new Error(`Unknown endpoint ${endpoint} for relayer service`);
+ }
+ }
+
+ return {
+ baseUrl: `http://localhost:${port}`,
+ path:
+ endpoint === 'rest'
+ ? '/status'
+ : endpoint === 'exposer'
+ ? '/config'
+ : ''
+ };
+ }
+
+ case 'registry': {
+ const port = config.registry?.ports?.rest;
+ if (!port) throw new Error('Registry REST port not found');
+
+ if (useIngress) {
+ return {
+ baseUrl: `https://registry.${host}`,
+ path: '/chains'
+ };
+ }
+
+ return {
+ baseUrl: `http://localhost:${port}`,
+ path: '/chains'
+ };
+ }
+
+ case 'explorer': {
+ const port = config.explorer?.ports?.http;
+ if (!port) throw new Error('Explorer HTTP port not found');
+
+ if (useIngress) {
+ return {
+ baseUrl: `https://explorer.${host}`,
+ path: ''
+ };
+ }
+
+ return {
+ baseUrl: `http://localhost:${port}`,
+ path: ''
+ };
+ }
+
+ default:
+ throw new Error(`Unknown service ${service}`);
+ }
+};