rework generic relayer re app config
This commit is contained in:
parent
40c2a3dc02
commit
e9b9c4fa35
|
@ -3,6 +3,7 @@ build
|
|||
**/node_modules
|
||||
/aptos/.aptos/**
|
||||
.idea
|
||||
.vscode
|
||||
.dccache
|
||||
.arcconfig
|
||||
*.iml
|
||||
|
|
|
@ -18,14 +18,7 @@ import { rootLogger } from "./log";
|
|||
import { processGenericRelayerVaa } from "./processor";
|
||||
import { Logger } from "winston";
|
||||
import * as deepCopy from "clone";
|
||||
import {
|
||||
ContractConfigEntry,
|
||||
getAppConfig,
|
||||
getContractsJson,
|
||||
getEnvironment,
|
||||
getEnvironmentOptions,
|
||||
init,
|
||||
} from "./env";
|
||||
import { loadAppConfig } from "./env";
|
||||
|
||||
export type GRContext = StandardRelayerContext & {
|
||||
relayProviders: Record<EVMChainId, string>;
|
||||
|
@ -33,25 +26,8 @@ export type GRContext = StandardRelayerContext & {
|
|||
};
|
||||
|
||||
async function main() {
|
||||
await init();
|
||||
const app = new StandardRelayerApp<GRContext>(
|
||||
getEnvironment(),
|
||||
getAppConfig()
|
||||
);
|
||||
const opts = getEnvironmentOptions();
|
||||
|
||||
// Build contract address maps
|
||||
const contracts = getContractsJson();
|
||||
const relayProviders = {} as Record<EVMChainId, string>;
|
||||
const wormholeRelayers = {} as Record<EVMChainId, string>;
|
||||
contracts.relayProviders.forEach(
|
||||
({ chainId, address }: ContractConfigEntry) =>
|
||||
(relayProviders[chainId] = address)
|
||||
);
|
||||
contracts.coreRelayers.forEach(
|
||||
({ chainId, address }: ContractConfigEntry) =>
|
||||
(wormholeRelayers[chainId] = address)
|
||||
);
|
||||
const { env, opts, relayProviders, wormholeRelayers } = await loadAppConfig();
|
||||
const app = new StandardRelayerApp<GRContext>(env, opts);
|
||||
|
||||
// Set up middleware
|
||||
app.use(async (ctx: GRContext, next: Next) => {
|
||||
|
|
|
@ -1,29 +1,25 @@
|
|||
import * as fs from "fs/promises";
|
||||
import yargs from "yargs";
|
||||
import * as Koa from "koa";
|
||||
import {
|
||||
Environment,
|
||||
Next,
|
||||
StandardRelayerApp,
|
||||
StandardRelayerContext,
|
||||
ProvidersOpts,
|
||||
RedisOptions,
|
||||
StandardRelayerAppOpts,
|
||||
} from "wormhole-relayer";
|
||||
import { defaultLogger } from "wormhole-relayer/lib/logging";
|
||||
import {
|
||||
CHAIN_ID_ETH,
|
||||
CHAIN_ID_BSC,
|
||||
EVMChainId,
|
||||
tryNativeToHexString,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { rootLogger } from "./log";
|
||||
import { processGenericRelayerVaa } from "./processor";
|
||||
import { Logger } from "winston";
|
||||
import * as deepCopy from "clone";
|
||||
import { ClusterOptions } from "ioredis";
|
||||
|
||||
export type Opts = {
|
||||
const SCRIPTS_DIR = "../../../ethereum/ts-scripts/relayer";
|
||||
|
||||
type Opts = {
|
||||
flag: Flag;
|
||||
};
|
||||
|
||||
export enum Flag {
|
||||
enum Flag {
|
||||
TiltKub = "tiltkub",
|
||||
Tilt = "tilt",
|
||||
Testnet = "testnet",
|
||||
|
@ -31,117 +27,201 @@ export enum Flag {
|
|||
Mainnet = "mainnet",
|
||||
}
|
||||
|
||||
export type ContractConfigEntry = { chainId: EVMChainId; address: "string" };
|
||||
export type ContractsJson = {
|
||||
type ContractConfigEntry = { chainId: EVMChainId; address: "string" };
|
||||
type ContractsJson = {
|
||||
relayProviders: ContractConfigEntry[];
|
||||
coreRelayers: ContractConfigEntry[];
|
||||
mockIntegrations: ContractConfigEntry[];
|
||||
};
|
||||
|
||||
export function getEnvironmentOptions() {
|
||||
interface GRRelayerAppConfig {
|
||||
contractsJsonPath: string;
|
||||
name: string;
|
||||
spyEndpoint: string;
|
||||
wormholeRpcs: [string];
|
||||
providers: ProvidersOpts;
|
||||
fetchSourceTxhash: boolean;
|
||||
logLevel: string;
|
||||
redis: RedisOptions;
|
||||
redisCluster?: StandardRelayerAppOpts["redisCluster"];
|
||||
redisClusterEndpoints?: StandardRelayerAppOpts["redisClusterEndpoints"];
|
||||
}
|
||||
|
||||
const defaults: { [key in Flag]: GRRelayerAppConfig } = {
|
||||
[Flag.TiltKub]: {
|
||||
name: "GenericRelayer",
|
||||
contractsJsonPath: `${SCRIPTS_DIR}/config/${Flag.TiltKub}/contracts.json`,
|
||||
spyEndpoint: "spy:7072",
|
||||
logLevel: "debug",
|
||||
wormholeRpcs: ["http://guardian:7071"],
|
||||
providers: {
|
||||
chains: {
|
||||
[CHAIN_ID_ETH]: {
|
||||
endpoints: ["http://eth-devnet:8545/"],
|
||||
},
|
||||
[CHAIN_ID_BSC]: {
|
||||
endpoints: ["http://eth-devnet2:8545/"],
|
||||
},
|
||||
},
|
||||
},
|
||||
fetchSourceTxhash: false,
|
||||
redis: { host: "redis", port: 6379 },
|
||||
},
|
||||
[Flag.Tilt]: {
|
||||
name: "GenericRelayer",
|
||||
contractsJsonPath: `${SCRIPTS_DIR}/config/${Flag.Tilt}/contracts.json`,
|
||||
logLevel: "debug",
|
||||
spyEndpoint: "localhost:7072",
|
||||
wormholeRpcs: ["http://localhost:7071"],
|
||||
providers: {
|
||||
chains: {
|
||||
[CHAIN_ID_ETH]: {
|
||||
endpoints: ["http://localhost:8545/"],
|
||||
},
|
||||
[CHAIN_ID_BSC]: {
|
||||
endpoints: ["http://localhost:8546/"],
|
||||
},
|
||||
},
|
||||
},
|
||||
fetchSourceTxhash: false,
|
||||
redis: {},
|
||||
},
|
||||
[Flag.K8sTestnet]: {} as any,
|
||||
[Flag.Testnet]: {} as any,
|
||||
[Flag.Mainnet]: {} as any,
|
||||
};
|
||||
|
||||
// async function loadAndMergeConfig(flag: Flag): Promise<GRRelayerAppConfig> {
|
||||
// const file = await fs.readFile(`./configs/${flag}.json`, {
|
||||
// encoding: "utf-8",
|
||||
// });
|
||||
// const config = JSON.parse(file);
|
||||
// return mergeDeep({}, [defaults[flag] ?? {}, config]) as GRRelayerAppConfig;
|
||||
// }
|
||||
|
||||
export async function loadAppConfig(): Promise<{
|
||||
env: Environment;
|
||||
opts: StandardRelayerAppOpts;
|
||||
relayProviders: Record<EVMChainId, string>;
|
||||
wormholeRelayers: Record<EVMChainId, string>;
|
||||
}> {
|
||||
const { flag } = getEnvironmentOptions();
|
||||
const config = await loadAndMergeConfig(flag);
|
||||
const contracts = await loadJson<ContractsJson>(config.contractsJsonPath);
|
||||
|
||||
const relayProviders = {} as Record<EVMChainId, string>;
|
||||
const wormholeRelayers = {} as Record<EVMChainId, string>;
|
||||
contracts.relayProviders.forEach(
|
||||
({ chainId, address }: ContractConfigEntry) =>
|
||||
(relayProviders[chainId] = address)
|
||||
);
|
||||
contracts.coreRelayers.forEach(
|
||||
({ chainId, address }: ContractConfigEntry) =>
|
||||
(wormholeRelayers[chainId] = address)
|
||||
);
|
||||
|
||||
return {
|
||||
relayProviders,
|
||||
wormholeRelayers,
|
||||
env: flagToEnvironment(flag),
|
||||
opts: {
|
||||
...config,
|
||||
privateKeys: privateKeys(contracts),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getEnvironmentOptions(): Opts {
|
||||
let opts = yargs(process.argv.slice(2)).argv as unknown as Opts;
|
||||
if (opts.flag == undefined) {
|
||||
opts.flag = process.env.GR_RE_FLAG as Flag;
|
||||
}
|
||||
if (!validateStringEnum(Flag, opts.flag)) {
|
||||
throw new Error("Unrecognized flag variant: " + opts.flag);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
let initialized = false;
|
||||
let contracts: ContractsJson;
|
||||
export async function init() {
|
||||
const opts = getEnvironmentOptions();
|
||||
contracts = await loadContractsJson(opts.flag);
|
||||
initialized = true;
|
||||
}
|
||||
function uninitialized() {
|
||||
throw new Error("init function was not called.");
|
||||
}
|
||||
function loadAndMergeConfig(flag: Flag): GRRelayerAppConfig {
|
||||
const base = defaults[flag];
|
||||
const isRedisCluster = !!process.env.REDIS_CLUSTER_ENDPOINTS;
|
||||
return {
|
||||
name: process.env.GENERIC_RELAYER_NAME || base.name,
|
||||
// env: process.env.NODE_ENV?.trim()?.toLowerCase() || "local",
|
||||
contractsJsonPath:
|
||||
process.env.CONTRACTS_JSON_PATH || base.contractsJsonPath,
|
||||
logLevel: process.env.LOG_LEVEL || base.logLevel,
|
||||
spyEndpoint: process.env.SPY_URL || base.spyEndpoint,
|
||||
wormholeRpcs: process.env.WORMHOLE_RPCS
|
||||
? JSON.parse(process.env.WORMHOLE_RPCS)
|
||||
: base.wormholeRpcs,
|
||||
providers: process.env.BLOCKCHAIN_PROVIDERS
|
||||
? JSON.parse(process.env.BLOCKCHAIN_PROVIDERS)
|
||||
: base.providers,
|
||||
fetchSourceTxhash: process.env.FETCH_SOURCE_TX_HASH
|
||||
? JSON.parse(process.env.FETCH_SOURCE_TX_HASH)
|
||||
: base.fetchSourceTxhash,
|
||||
// concurrency: Number(process.env.RELAY_CONCURRENCY) || 5,
|
||||
// influx: {
|
||||
// url: process.env.INFLUXDB_URL,
|
||||
// org: process.env.INFLUXDB_ORG,
|
||||
// bucket: process.env.INFLUXDB_BUCKET,
|
||||
// token: process.env.INFLUXDB_TOKEN,
|
||||
// },
|
||||
|
||||
export function getAppConfig() {
|
||||
const contracts = getContractsJson();
|
||||
const options = getEnvironmentOptions();
|
||||
if (options.flag == Flag.TiltKub) {
|
||||
return {
|
||||
name: "GenericRelayer",
|
||||
privateKeys: privateKeys(contracts),
|
||||
spyEndpoint: "spy:7072",
|
||||
wormholeRpcs: ["http://guardian:7071"],
|
||||
providers: {
|
||||
chains: {
|
||||
[CHAIN_ID_ETH]: {
|
||||
endpoints: ["http://eth-devnet:8545/"],
|
||||
redisClusterEndpoints: process.env.REDIS_CLUSTER_ENDPOINTS?.split(","), // "url1:port,url2:port"
|
||||
redisCluster: isRedisCluster
|
||||
? <ClusterOptions>{
|
||||
dnsLookup: (address: any, callback: any) => callback(null, address),
|
||||
slotsRefreshTimeout: 1000,
|
||||
redisOptions: {
|
||||
tls: process.env.REDIS_TLS ? {} : undefined,
|
||||
username: process.env.REDIS_USERNAME,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
},
|
||||
[CHAIN_ID_BSC]: {
|
||||
endpoints: ["http://eth-devnet2:8545/"],
|
||||
},
|
||||
},
|
||||
},
|
||||
logger: defaultLogger,
|
||||
fetchSourceTxhash: false,
|
||||
redis: { host: "redis", port: 6379 },
|
||||
// redisCluster: {},
|
||||
// redisClusterEndpoints: [],
|
||||
};
|
||||
|
||||
//else assume localhost / tilt
|
||||
} else {
|
||||
return {
|
||||
name: "GenericRelayer",
|
||||
privateKeys: privateKeys(contracts),
|
||||
spyEndpoint: "localhost:7072",
|
||||
wormholeRpcs: ["http://localhost:7071"],
|
||||
providers: {
|
||||
chains: {
|
||||
[CHAIN_ID_ETH]: {
|
||||
endpoints: ["http://localhost:8545/"],
|
||||
},
|
||||
[CHAIN_ID_BSC]: {
|
||||
endpoints: ["http://localhost:8546/"],
|
||||
},
|
||||
},
|
||||
},
|
||||
logger: defaultLogger,
|
||||
fetchSourceTxhash: false,
|
||||
redis: {},
|
||||
// redisCluster: {},
|
||||
// redisClusterEndpoints: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
redis: <RedisOptions>{
|
||||
tls: process.env.REDIS_TLS ? {} : undefined,
|
||||
host: process.env.REDIS_HOST ? undefined : process.env.REDIS_HOST,
|
||||
port: process.env.REDIS_CLUSTER_ENDPOINTS
|
||||
? undefined
|
||||
: Number(process.env.REDIS_PORT) || undefined,
|
||||
username: process.env.REDIS_USERNAME,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
//internal only
|
||||
async function loadContractsJson(flag: Flag): Promise<ContractsJson> {
|
||||
if ((flag = Flag.TiltKub)) {
|
||||
flag = Flag.Tilt; //TiltKub contracts are the same as tilt
|
||||
}
|
||||
return JSON.parse(
|
||||
await fs.readFile(`${SCRIPTS_DIR}/config/${flag}/contracts.json`, {
|
||||
encoding: "utf-8",
|
||||
})
|
||||
) as ContractsJson;
|
||||
}
|
||||
|
||||
export function getContractsJson(): ContractsJson {
|
||||
if (!initialized || !contracts) {
|
||||
uninitialized();
|
||||
}
|
||||
return contracts;
|
||||
}
|
||||
|
||||
function privateKeys(contracts: ContractsJson) {
|
||||
function privateKeys(contracts: ContractsJson): {
|
||||
[k in Partial<EVMChainId>]: string[];
|
||||
} {
|
||||
const chainIds = new Set(contracts.coreRelayers.map((r) => r.chainId));
|
||||
//TODO not this
|
||||
const privateKey =
|
||||
"6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1"; //private key 1 for tilt //process.env["PRIVATE_KEY"]! as string;
|
||||
const privateKeys = {} as Record<EVMChainId, [string]>;
|
||||
let privateKeysArray = [] as string[];
|
||||
if (process.env.EVM_PRIVATE_KEYS) {
|
||||
privateKeysArray = JSON.parse(process.env.EVM_PRIVATE_KEYS);
|
||||
} else if (process.env.EVM_PRIVATE_KEY) {
|
||||
privateKeysArray = [process.env.EVM_PRIVATE_KEY];
|
||||
} else if (process.env.PRIVATE_KEY) {
|
||||
// tilt
|
||||
privateKeysArray = [process.env.PRIVATE_KEY];
|
||||
} else {
|
||||
// Todo: remove this
|
||||
// tilt evm private key
|
||||
console.log(
|
||||
"Warning: using tilt private key because no others were specified"
|
||||
);
|
||||
privateKeysArray = [
|
||||
"6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1",
|
||||
];
|
||||
}
|
||||
const privateKeys = {} as Record<EVMChainId, string[]>;
|
||||
for (const chainId of chainIds) {
|
||||
privateKeys[chainId] = [privateKey];
|
||||
privateKeys[chainId] = privateKeysArray;
|
||||
}
|
||||
return privateKeys;
|
||||
}
|
||||
|
||||
export function getEnvironment() {
|
||||
const options = getEnvironmentOptions();
|
||||
return flagToEnvironment(options.flag);
|
||||
}
|
||||
|
||||
function flagToEnvironment(flag: Flag): Environment {
|
||||
switch (flag) {
|
||||
case Flag.K8sTestnet:
|
||||
|
@ -157,4 +237,22 @@ function flagToEnvironment(flag: Flag): Environment {
|
|||
}
|
||||
}
|
||||
|
||||
const SCRIPTS_DIR = "../../../ethereum/ts-scripts/relayer";
|
||||
function validateStringEnum<O extends Object>(
|
||||
enumObject: O,
|
||||
passed: string
|
||||
): boolean {
|
||||
for (const value of Object.values(enumObject)) {
|
||||
if (value === passed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadJson<T>(path: string): Promise<T> {
|
||||
return fs
|
||||
.readFile(path, {
|
||||
encoding: "utf-8",
|
||||
})
|
||||
.then(JSON.parse) as Promise<T>;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { GRContext } from "./app";
|
|||
|
||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||
import { GetARGsTypeFromFactory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts/commons";
|
||||
import { getAppConfig } from "./env";
|
||||
import { loadAppConfig } from "./env";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export async function processGenericRelayerVaa(ctx: GRContext, next: Next) {
|
||||
|
@ -60,7 +60,7 @@ async function processDelivery(ctx: GRContext) {
|
|||
|
||||
const results: Uint8Array[] = [];
|
||||
|
||||
const appConfig = getAppConfig();
|
||||
const appConfig = loadAppConfig();
|
||||
|
||||
//TODO not anything even resembling this
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue