Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/src/network-definition-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ The network config can be provided both in `json` or `toml` format and each sect
- name: (String) name of the `env` var.
- value: (String| number) Value of the env var.
- `keystore_key_types`: Defines which keystore keys should be created, for more details checkout details below.
- `override_eth_key`: (String) Overrides the auto-generated ethereum session key for EVM-based paras. When set, the provided key is used and Zombienet omits the random seed from the resulting `zombie.json`.

- `collator_groups`:

Expand All @@ -126,6 +127,7 @@ The network config can be provided both in `json` or `toml` format and each sect
- name: (String) name of the `env` var.
- value: (String| number) Value of the env var.
- `substrate_cli_args_version`: (0|1|2) By default zombienet will evaluate your binary and set the correct version, but that produces a small overhead that could be skipped if you set directly with this key.
- `override_eth_key`: (String) Same as above, lets you provide the ethereum session key.

- `onboard_as_parachain`: (Boolean, default true) flag to specify whether the para should be onboarded as a parachain or stay a parathread
- `register_para`: (Boolean, default true) flag to specify whether the para should be registered. The `add_to_genesis` flag **must** be set to false for this flag to have any effect.
Expand Down
26 changes: 26 additions & 0 deletions examples/0010-generic-evm-override-eth-key.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[settings]
timeout = 1000

[relaychain]
default_image = "docker.io/paritypr/polkadot-debug:master"
default_command = "polkadot"
default_args = ["-lparachain=debug"]

chain = "rococo-local"

[[relaychain.nodes]]
name = "alice"

[[relaychain.nodes]]
name = "bob"

[[parachains]]
id = 2000
chain = "generic-evm"

[parachains.collator]
name = "evm-collator"
image = "docker.io/parity/polkadot-parachain:latest"
command = "polkadot-parachain"
args = ["-lparachain=debug"]
override_eth_key = "0xf2b3f8c5c3d9e2f1a4b6c8d0e3f5a7b9c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keyring } from "@polkadot/api";
import type { KeyringPair } from "@polkadot/keyring/types";
import { u8aToHex } from "@polkadot/util";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { CreateLogTable, decorators } from "@zombienet/utils";
Expand All @@ -8,24 +9,44 @@ import {
readAndParseChainSpec,
writeChainSpec,
} from "../chainSpec";
import { generateKeyForNode as _generateKeyForNode } from "../keys";
import {
type NodeAccounts,
generateKeyForNode as _generateKeyForNode,
} from "../keys";
import { Node } from "../sharedTypes";

async function generateKeyForNode(nodeName?: string): Promise<any> {
async function generateKeyForNode(
nodeName?: string,
overrideEthKey?: string,
): Promise<NodeAccounts> {
const keys = await _generateKeyForNode(nodeName);

await cryptoWaitReady();

const eth_keyring = new Keyring({ type: "ethereum" });
const eth_account = eth_keyring.createFromUri(
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
);
const uri = overrideEthKey
? overrideEthKey
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;

let eth_account: KeyringPair;
try {
eth_account = eth_keyring.createFromUri(uri);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the naming is somehow misleading, since the key is used as seed for generating the keypair.
ping @aurexav what's your expected behavior?

} catch (error) {
throw new Error(
`Failed to create ethereum session key for ${nodeName}: ${error}`,
);
}

keys.eth_account = {
address: eth_account.address,
publicKey: u8aToHex(eth_account.publicKey),
};

if (overrideEthKey) {
keys.ethKeyOverrideUsed = true;
delete keys.mnemonic;
}

return keys;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keyring } from "@polkadot/api";
import type { KeyringPair } from "@polkadot/keyring/types";
import { u8aToHex } from "@polkadot/util";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { CreateLogTable, decorators } from "@zombienet/utils";
Expand All @@ -8,24 +9,44 @@ import {
readAndParseChainSpec,
writeChainSpec,
} from "../chainSpec";
import { generateKeyForNode as _generateKeyForNode } from "../keys";
import {
type NodeAccounts,
generateKeyForNode as _generateKeyForNode,
} from "../keys";
import { Node } from "../sharedTypes";

async function generateKeyForNode(nodeName?: string): Promise<any> {
async function generateKeyForNode(
nodeName?: string,
overrideEthKey?: string,
): Promise<NodeAccounts> {
const keys = await _generateKeyForNode(nodeName);

await cryptoWaitReady();

const eth_keyring = new Keyring({ type: "ethereum" });
const eth_account = eth_keyring.createFromUri(
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
);
const uri = overrideEthKey
? overrideEthKey
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;

let eth_account: KeyringPair;
try {
eth_account = eth_keyring.createFromUri(uri);
} catch (error) {
throw new Error(
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
);
}

keys.eth_account = {
address: eth_account.address,
publicKey: u8aToHex(eth_account.publicKey),
};

if (overrideEthKey) {
keys.ethKeyOverrideUsed = true;
delete keys.mnemonic;
}

return keys;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keyring } from "@polkadot/api";
import type { KeyringPair } from "@polkadot/keyring/types";
import { u8aToHex } from "@polkadot/util";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { CreateLogTable, decorators } from "@zombienet/utils";
Expand All @@ -8,24 +9,44 @@ import {
readAndParseChainSpec,
writeChainSpec,
} from "../chainSpec";
import { generateKeyForNode as _generateKeyForNode } from "../keys";
import {
type NodeAccounts,
generateKeyForNode as _generateKeyForNode,
} from "../keys";
import { Node } from "../sharedTypes";

async function generateKeyForNode(nodeName?: string): Promise<any> {
async function generateKeyForNode(
nodeName?: string,
overrideEthKey?: string,
): Promise<NodeAccounts> {
const keys = await _generateKeyForNode(nodeName);

await cryptoWaitReady();

const eth_keyring = new Keyring({ type: "ethereum" });
const eth_account = eth_keyring.createFromUri(
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
);
const uri = overrideEthKey
? overrideEthKey
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;

let eth_account: KeyringPair;
try {
eth_account = eth_keyring.createFromUri(uri);
} catch (error) {
throw new Error(
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
);
}

keys.eth_account = {
address: eth_account.address,
publicKey: u8aToHex(eth_account.publicKey),
};

if (overrideEthKey) {
keys.ethKeyOverrideUsed = true;
delete keys.mnemonic;
}

return keys;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keyring } from "@polkadot/api";
import type { KeyringPair } from "@polkadot/keyring/types";
import { u8aToHex } from "@polkadot/util";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { CreateLogTable, decorators } from "@zombienet/utils";
Expand All @@ -9,7 +10,10 @@ import {
readAndParseChainSpec,
writeChainSpec,
} from "../chainSpec";
import { generateKeyForNode as _generateKeyForNode } from "../keys";
import {
type NodeAccounts,
generateKeyForNode as _generateKeyForNode,
} from "../keys";
import { ChainSpec } from "../types";
import { Node } from "../sharedTypes";

Expand Down Expand Up @@ -96,23 +100,42 @@ async function clearAuthorities(specPath: string) {
writeChainSpec(specPath, chainSpec);
}

async function generateKeyForNode(nodeName?: string): Promise<any> {
async function generateKeyForNode(
nodeName?: string,
overrideEthKey?: string,
): Promise<NodeAccounts> {
const keys = await _generateKeyForNode(nodeName);

await cryptoWaitReady();

const eth_keyring = new Keyring({ type: "ethereum" });
const eth_account = eth_keyring.createFromUri(
nodeName && nodeName.toLocaleLowerCase() in KNOWN_MOONBEAM_KEYS
? KNOWN_MOONBEAM_KEYS[nodeName.toLocaleLowerCase()]
: `${keys.mnemonic}/m/44'/60'/0'/0/0`,
);
const lowerName = nodeName?.toLocaleLowerCase();
const knownKey = lowerName ? KNOWN_MOONBEAM_KEYS[lowerName] : undefined;
const uri = knownKey
? knownKey
: overrideEthKey
? overrideEthKey
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;

let eth_account: KeyringPair;
try {
eth_account = eth_keyring.createFromUri(uri);
} catch (error) {
throw new Error(
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
);
}

keys.eth_account = {
address: eth_account.address,
publicKey: u8aToHex(eth_account.publicKey),
};

if (overrideEthKey) {
keys.ethKeyOverrideUsed = true;
delete keys.mnemonic;
}

return keys;
}

Expand Down
5 changes: 4 additions & 1 deletion javascript/packages/orchestrator/src/configGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,10 @@ async function getCollatorNodeFromConfig(

const collatorName = getUniqueName(collatorConfig.name || "collator");
const [decoratedKeysGenerator] = decorate(para, [generateKeyForNode]);
const accountsForNode = await decoratedKeysGenerator(collatorName);
const accountsForNode = await decoratedKeysGenerator(
collatorName,
collatorConfig.override_eth_key,
);

const provider = networkSpec.settings.provider;
const ports = await getPorts(provider, collatorConfig);
Expand Down
26 changes: 23 additions & 3 deletions javascript/packages/orchestrator/src/keys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keyring } from "@polkadot/api";
import type { KeyringPair } from "@polkadot/keyring/types";
import { u8aToHex } from "@polkadot/util";
import {
cryptoWaitReady,
Expand All @@ -13,14 +14,34 @@ function nameCase(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

export async function generateKeyFromSeed(seed: string): Promise<any> {
export interface SigningAccount {
address: string;
publicKey: string;
}

export interface NodeAccounts {
seed?: string;
mnemonic?: string;
sr_account: SigningAccount;
sr_stash: SigningAccount;
ed_account: SigningAccount;
ec_account: {
publicKey: string;
};
eth_account?: SigningAccount;
ethKeyOverrideUsed?: boolean;
}

export async function generateKeyFromSeed(seed: string): Promise<KeyringPair> {
await cryptoWaitReady();

const sr_keyring = new Keyring({ type: "sr25519" });
return sr_keyring.createFromUri(`//${seed}`);
}

export async function generateKeyForNode(nodeName?: string): Promise<any> {
export async function generateKeyForNode(
nodeName?: string,
): Promise<NodeAccounts> {
await cryptoWaitReady();

const mnemonic = mnemonicGenerate();
Expand All @@ -38,7 +59,6 @@ export async function generateKeyForNode(nodeName?: string): Promise<any> {
const ec_keyring = new Keyring({ type: "ecdsa" });
const ec_account = ec_keyring.createFromUri(`${seed}`);

// return the needed info
return {
seed,
mnemonic,
Expand Down
1 change: 1 addition & 0 deletions javascript/packages/orchestrator/src/sharedTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export interface NodeConfig extends NodeCommonTypes {
prometheus_port?: number;
p2p_cert_hash?: string; // libp2p certhash to use with webrtc transport.
delay_network_settings?: DelayNetworkSettings;
override_eth_key?: string;
}

export interface NodeCommonTypes {
Expand Down
8 changes: 8 additions & 0 deletions javascript/packages/orchestrator/src/spawner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ export const spawnNode = async (
isAssetHubPolkadot,
);
keystoreLocalDir = path.dirname(keystoreFiles[0]);

// When we have `override_eth_key` option
// seeds/mnemonics that should not leak into `zombie.json`;
if (node.accounts.ethKeyOverrideUsed) {
delete node.accounts.seed;
delete node.accounts.mnemonic;
delete node.accounts.ethKeyOverrideUsed;
}
}

// replace all network references in command
Expand Down
Loading