Skip to content

Commit

Permalink
feat: Multi EVM Chain Signers (#4922)
Browse files Browse the repository at this point in the history
### Description
This PR modifies the warp init command to read the multiProtocolSigner
from the signer middleware instead of creating a new instance.

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

-  #4910
- Fixes issue with duplicate signer initialization in warp route
configuration
- Improves consistency with other commands that use signer middleware

### Backward compatibility

Yes - This change is backward compatible as it maintains the same
functionality while making the multiProtocolSigner parameter optional.

### Testing

Manual testing

---------

Co-authored-by: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com>
Co-authored-by: ljankovic-txfusion <lazar@txfusion.io>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent a29a899 commit 79f8197
Show file tree
Hide file tree
Showing 34 changed files with 1,112 additions and 75 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-balloons-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/utils': minor
---

Added `isPrivateKeyEvm` function for validating EVM private keys
5 changes: 5 additions & 0 deletions .changeset/spicy-gifts-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/cli': minor
---

Added strategy management CLI commands and MultiProtocolSigner implementation for flexible cross-chain signer configuration and management
7 changes: 6 additions & 1 deletion typescript/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ import {
overrideRegistryUriCommandOption,
registryUriCommandOption,
skipConfirmationOption,
strategyCommandOption,
} from './src/commands/options.js';
import { registryCommand } from './src/commands/registry.js';
import { relayerCommand } from './src/commands/relayer.js';
import { sendCommand } from './src/commands/send.js';
import { statusCommand } from './src/commands/status.js';
import { strategyCommand } from './src/commands/strategy.js';
import { submitCommand } from './src/commands/submit.js';
import { validatorCommand } from './src/commands/validator.js';
import { warpCommand } from './src/commands/warp.js';
import { contextMiddleware } from './src/context/context.js';
import { contextMiddleware, signerMiddleware } from './src/context/context.js';
import { configureLogger, errorRed } from './src/logger.js';
import { checkVersion } from './src/utils/version-check.js';
import { VERSION } from './src/version.js';
Expand All @@ -49,12 +51,14 @@ try {
.option('key', keyCommandOption)
.option('disableProxy', disableProxyCommandOption)
.option('yes', skipConfirmationOption)
.option('strategy', strategyCommandOption)
.global(['log', 'verbosity', 'registry', 'overrides', 'yes'])
.middleware([
(argv) => {
configureLogger(argv.log as LogFormat, argv.verbosity as LogLevel);
},
contextMiddleware,
signerMiddleware,
])
.command(avsCommand)
.command(configCommand)
Expand All @@ -66,6 +70,7 @@ try {
.command(relayerCommand)
.command(sendCommand)
.command(statusCommand)
.command(strategyCommand)
.command(submitCommand)
.command(validatorCommand)
.command(warpCommand)
Expand Down
15 changes: 15 additions & 0 deletions typescript/cli/src/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CommandModule } from 'yargs';
import { readChainConfigs } from '../config/chain.js';
import { readIsmConfig } from '../config/ism.js';
import { readMultisigConfig } from '../config/multisig.js';
import { readChainSubmissionStrategyConfig } from '../config/strategy.js';
import { readWarpRouteDeployConfig } from '../config/warp.js';
import { CommandModuleWithContext } from '../context/types.js';
import { log, logGreen } from '../logger.js';
Expand Down Expand Up @@ -31,6 +32,7 @@ const validateCommand: CommandModule = {
.command(validateChainCommand)
.command(validateIsmCommand)
.command(validateIsmAdvancedCommand)
.command(validateStrategyCommand)
.command(validateWarpCommand)
.version(false)
.demandCommand(),
Expand Down Expand Up @@ -76,6 +78,19 @@ const validateIsmAdvancedCommand: CommandModuleWithContext<{ path: string }> = {
},
};

const validateStrategyCommand: CommandModuleWithContext<{ path: string }> = {
command: 'strategy',
describe: 'Validates a Strategy config file',
builder: {
path: inputFileCommandOption(),
},
handler: async ({ path }) => {
await readChainSubmissionStrategyConfig(path);
logGreen('Config is valid');
process.exit(0);
},
};

const validateWarpCommand: CommandModuleWithContext<{ path: string }> = {
command: 'warp',
describe: 'Validate a Warp Route deployment config file',
Expand Down
5 changes: 3 additions & 2 deletions typescript/cli/src/commands/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH =
'./configs/warp-route-deployment.yaml';

export const DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH = './configs/core-config.yaml';
export const DEFAULT_STRATEGY_CONFIG_PATH = `${os.homedir()}/.hyperlane/strategies/default-strategy.yaml`;

export const warpDeploymentConfigCommandOption: Options = {
type: 'string',
Expand Down Expand Up @@ -196,8 +197,8 @@ export const transactionsCommandOption: Options = {
export const strategyCommandOption: Options = {
type: 'string',
description: 'The submission strategy input file path.',
alias: 's',
demandOption: true,
alias: ['s', 'strategy'],
demandOption: false,
};

export const addressCommandOption = (
Expand Down
29 changes: 27 additions & 2 deletions typescript/cli/src/commands/signCommands.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
// Commands that send tx and require a key to sign.
// It's useful to have this listed here so the context
// middleware can request keys up front when required.
export const SIGN_COMMANDS = ['deploy', 'send', 'status', 'submit', 'relayer'];
export const SIGN_COMMANDS = [
'apply',
'deploy',
'send',
'status',
'submit',
'relayer',
];

export function isSignCommand(argv: any): boolean {
//TODO: fix reading and checking warp without signer, and remove this
const temporarySignCommandsCheck =
argv._[0] === 'warp' && (argv._[1] === 'read' || argv._[1] === 'check');
return (
SIGN_COMMANDS.includes(argv._[0]) ||
(argv._.length > 1 && SIGN_COMMANDS.includes(argv._[1]))
(argv._.length > 1 && SIGN_COMMANDS.includes(argv._[1])) ||
temporarySignCommandsCheck
);
}

export enum CommandType {
WARP_DEPLOY = 'warp:deploy',
WARP_SEND = 'warp:send',
WARP_APPLY = 'warp:apply',
WARP_READ = 'warp:read',
WARP_CHECK = 'warp:check',
SEND_MESSAGE = 'send:message',
AGENT_KURTOSIS = 'deploy:kurtosis-agents',
STATUS = 'status:',
SUBMIT = 'submit:',
RELAYER = 'relayer:',
CORE_APPLY = 'core:apply',
}
70 changes: 70 additions & 0 deletions typescript/cli/src/commands/strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { stringify as yamlStringify } from 'yaml';
import { CommandModule } from 'yargs';

import {
createStrategyConfig,
readChainSubmissionStrategyConfig,
} from '../config/strategy.js';
import { CommandModuleWithWriteContext } from '../context/types.js';
import { log, logCommandHeader } from '../logger.js';
import { indentYamlOrJson } from '../utils/files.js';
import { maskSensitiveData } from '../utils/output.js';

import {
DEFAULT_STRATEGY_CONFIG_PATH,
outputFileCommandOption,
strategyCommandOption,
} from './options.js';

/**
* Parent command
*/
export const strategyCommand: CommandModule = {
command: 'strategy',
describe: 'Manage Hyperlane deployment strategies',
builder: (yargs) =>
yargs.command(init).command(read).version(false).demandCommand(),
handler: () => log('Command required'),
};

export const init: CommandModuleWithWriteContext<{
out: string;
}> = {
command: 'init',
describe: 'Creates strategy configuration',
builder: {
out: outputFileCommandOption(DEFAULT_STRATEGY_CONFIG_PATH),
},
handler: async ({ context, out }) => {
logCommandHeader(`Hyperlane Strategy Init`);

await createStrategyConfig({
context,
outPath: out,
});
process.exit(0);
},
};

export const read: CommandModuleWithWriteContext<{
strategy: string;
}> = {
command: 'read',
describe: 'Reads strategy configuration',
builder: {
strategy: {
...strategyCommandOption,
demandOption: true,
default: DEFAULT_STRATEGY_CONFIG_PATH,
},
},
handler: async ({ strategy: strategyUrl }) => {
logCommandHeader(`Hyperlane Strategy Read`);

const strategy = await readChainSubmissionStrategyConfig(strategyUrl);
const maskedConfig = maskSensitiveData(strategy);
log(indentYamlOrJson(yamlStringify(maskedConfig, null, 2), 4));

process.exit(0);
},
};
4 changes: 2 additions & 2 deletions typescript/cli/src/config/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function createCoreDeployConfig({
logBlue('Creating a new core deployment config...');

const owner = await detectAndConfirmOrPrompt(
async () => context.signer?.getAddress(),
async () => context.signerAddress,
ENTER_DESIRED_VALUE_MSG,
'owner address',
SIGNER_PROMPT_LABEL,
Expand All @@ -64,7 +64,7 @@ export async function createCoreDeployConfig({
});
proxyAdmin = {
owner: await detectAndConfirmOrPrompt(
async () => context.signer?.getAddress(),
async () => context.signerAddress,
ENTER_DESIRED_VALUE_MSG,
'ProxyAdmin owner address',
SIGNER_PROMPT_LABEL,
Expand Down
6 changes: 3 additions & 3 deletions typescript/cli/src/config/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ async function getOwnerAndBeneficiary(
advanced: boolean,
) {
const unnormalizedOwner =
!advanced && context.signer
? await context.signer.getAddress()
!advanced && context.signerAddress
? context.signerAddress
: await detectAndConfirmOrPrompt(
async () => context.signer?.getAddress(),
async () => context.signerAddress,
`For ${module}, enter`,
'owner address',
'signer',
Expand Down
6 changes: 3 additions & 3 deletions typescript/cli/src/config/ism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ export const createTrustedRelayerConfig = callWithConfigCreationLogs(
advanced: boolean = false,
): Promise<TrustedRelayerIsmConfig> => {
const relayer =
!advanced && context.signer
? await context.signer.getAddress()
!advanced && context.signerAddress
? context.signerAddress
: await detectAndConfirmOrPrompt(
async () => context.signer?.getAddress(),
async () => context.signerAddress,
'For trusted relayer ISM, enter',
'relayer address',
'signer',
Expand Down
Loading

0 comments on commit 79f8197

Please sign in to comment.