Skip to content

Commit 834a05e

Browse files
committed
feat: add support for overriding ethereum session key
1 parent f849da7 commit 834a05e

File tree

10 files changed

+177
-30
lines changed

10 files changed

+177
-30
lines changed

docs/src/network-definition-spec.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ The network config can be provided both in `json` or `toml` format and each sect
112112
- name: (String) name of the `env` var.
113113
- value: (String| number) Value of the env var.
114114
- `keystore_key_types`: Defines which keystore keys should be created, for more details checkout details below.
115+
- `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`.
115116

116117
- `collator_groups`:
117118

@@ -126,6 +127,7 @@ The network config can be provided both in `json` or `toml` format and each sect
126127
- name: (String) name of the `env` var.
127128
- value: (String| number) Value of the env var.
128129
- `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.
130+
- `override_eth_key`: (String) Same as above, lets you provide the ethereum session key.
129131

130132
- `onboard_as_parachain`: (Boolean, default true) flag to specify whether the para should be onboarded as a parachain or stay a parathread
131133
- `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.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[settings]
2+
timeout = 1000
3+
4+
[relaychain]
5+
default_image = "docker.io/paritypr/polkadot-debug:master"
6+
default_command = "polkadot"
7+
default_args = ["-lparachain=debug"]
8+
9+
chain = "rococo-local"
10+
11+
[[relaychain.nodes]]
12+
name = "alice"
13+
14+
[[relaychain.nodes]]
15+
name = "bob"
16+
17+
[[parachains]]
18+
id = 2000
19+
chain = "generic-evm"
20+
21+
[parachains.collator]
22+
name = "evm-collator"
23+
image = "docker.io/parity/polkadot-parachain:latest"
24+
command = "polkadot-parachain"
25+
args = ["-lparachain=debug"]
26+
override_eth_key = "0xf2b3f8c5c3d9e2f1a4b6c8d0e3f5a7b9c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"

javascript/packages/orchestrator/src/chain-decorators/generic-evm.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Keyring } from "@polkadot/api";
2+
import type { KeyringPair } from "@polkadot/keyring/types";
23
import { u8aToHex } from "@polkadot/util";
34
import { cryptoWaitReady } from "@polkadot/util-crypto";
45
import { CreateLogTable, decorators } from "@zombienet/utils";
@@ -8,24 +9,44 @@ import {
89
readAndParseChainSpec,
910
writeChainSpec,
1011
} from "../chainSpec";
11-
import { generateKeyForNode as _generateKeyForNode } from "../keys";
12+
import {
13+
type NodeAccounts,
14+
generateKeyForNode as _generateKeyForNode,
15+
} from "../keys";
1216
import { Node } from "../sharedTypes";
1317

14-
async function generateKeyForNode(nodeName?: string): Promise<any> {
15-
const keys = await _generateKeyForNode(nodeName);
18+
async function generateKeyForNode(
19+
nodeName?: string,
20+
overrideEthKey?: string,
21+
): Promise<NodeAccounts> {
22+
const keys = await _generateKeyForNode(nodeName, overrideEthKey);
1623

1724
await cryptoWaitReady();
1825

1926
const eth_keyring = new Keyring({ type: "ethereum" });
20-
const eth_account = eth_keyring.createFromUri(
21-
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
22-
);
27+
const uri = overrideEthKey
28+
? overrideEthKey
29+
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;
30+
31+
let eth_account: KeyringPair;
32+
try {
33+
eth_account = eth_keyring.createFromUri(uri);
34+
} catch (error) {
35+
throw new Error(
36+
`Failed to create ethereum session key for ${nodeName}: ${error}`,
37+
);
38+
}
2339

2440
keys.eth_account = {
2541
address: eth_account.address,
2642
publicKey: u8aToHex(eth_account.publicKey),
2743
};
2844

45+
if (overrideEthKey) {
46+
keys.ethKeyOverrideUsed = true;
47+
delete keys.mnemonic;
48+
}
49+
2950
return keys;
3051
}
3152

javascript/packages/orchestrator/src/chain-decorators/local-v.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Keyring } from "@polkadot/api";
2+
import type { KeyringPair } from "@polkadot/keyring/types";
23
import { u8aToHex } from "@polkadot/util";
34
import { cryptoWaitReady } from "@polkadot/util-crypto";
45
import { CreateLogTable, decorators } from "@zombienet/utils";
@@ -8,24 +9,44 @@ import {
89
readAndParseChainSpec,
910
writeChainSpec,
1011
} from "../chainSpec";
11-
import { generateKeyForNode as _generateKeyForNode } from "../keys";
12+
import {
13+
type NodeAccounts,
14+
generateKeyForNode as _generateKeyForNode,
15+
} from "../keys";
1216
import { Node } from "../sharedTypes";
1317

14-
async function generateKeyForNode(nodeName?: string): Promise<any> {
15-
const keys = await _generateKeyForNode(nodeName);
18+
async function generateKeyForNode(
19+
nodeName?: string,
20+
overrideEthKey?: string,
21+
): Promise<NodeAccounts> {
22+
const keys = await _generateKeyForNode(nodeName, overrideEthKey);
1623

1724
await cryptoWaitReady();
1825

1926
const eth_keyring = new Keyring({ type: "ethereum" });
20-
const eth_account = eth_keyring.createFromUri(
21-
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
22-
);
27+
const uri = overrideEthKey
28+
? overrideEthKey
29+
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;
30+
31+
let eth_account: KeyringPair;
32+
try {
33+
eth_account = eth_keyring.createFromUri(uri);
34+
} catch (error) {
35+
throw new Error(
36+
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
37+
);
38+
}
2339

2440
keys.eth_account = {
2541
address: eth_account.address,
2642
publicKey: u8aToHex(eth_account.publicKey),
2743
};
2844

45+
if (overrideEthKey) {
46+
keys.ethKeyOverrideUsed = true;
47+
delete keys.mnemonic;
48+
}
49+
2950
return keys;
3051
}
3152

javascript/packages/orchestrator/src/chain-decorators/mainnet-local-v.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Keyring } from "@polkadot/api";
2+
import type { KeyringPair } from "@polkadot/keyring/types";
23
import { u8aToHex } from "@polkadot/util";
34
import { cryptoWaitReady } from "@polkadot/util-crypto";
45
import { CreateLogTable, decorators } from "@zombienet/utils";
@@ -8,24 +9,44 @@ import {
89
readAndParseChainSpec,
910
writeChainSpec,
1011
} from "../chainSpec";
11-
import { generateKeyForNode as _generateKeyForNode } from "../keys";
12+
import {
13+
type NodeAccounts,
14+
generateKeyForNode as _generateKeyForNode,
15+
} from "../keys";
1216
import { Node } from "../sharedTypes";
1317

14-
async function generateKeyForNode(nodeName?: string): Promise<any> {
15-
const keys = await _generateKeyForNode(nodeName);
18+
async function generateKeyForNode(
19+
nodeName?: string,
20+
overrideEthKey?: string,
21+
): Promise<NodeAccounts> {
22+
const keys = await _generateKeyForNode(nodeName, overrideEthKey);
1623

1724
await cryptoWaitReady();
1825

1926
const eth_keyring = new Keyring({ type: "ethereum" });
20-
const eth_account = eth_keyring.createFromUri(
21-
`${keys.mnemonic}/m/44'/60'/0'/0/0`,
22-
);
27+
const uri = overrideEthKey
28+
? overrideEthKey
29+
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;
30+
31+
let eth_account: KeyringPair;
32+
try {
33+
eth_account = eth_keyring.createFromUri(uri);
34+
} catch (error) {
35+
throw new Error(
36+
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
37+
);
38+
}
2339

2440
keys.eth_account = {
2541
address: eth_account.address,
2642
publicKey: u8aToHex(eth_account.publicKey),
2743
};
2844

45+
if (overrideEthKey) {
46+
keys.ethKeyOverrideUsed = true;
47+
delete keys.mnemonic;
48+
}
49+
2950
return keys;
3051
}
3152

javascript/packages/orchestrator/src/chain-decorators/moonbeam.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Keyring } from "@polkadot/api";
2+
import type { KeyringPair } from "@polkadot/keyring/types";
23
import { u8aToHex } from "@polkadot/util";
34
import { cryptoWaitReady } from "@polkadot/util-crypto";
45
import { CreateLogTable, decorators } from "@zombienet/utils";
@@ -9,7 +10,10 @@ import {
910
readAndParseChainSpec,
1011
writeChainSpec,
1112
} from "../chainSpec";
12-
import { generateKeyForNode as _generateKeyForNode } from "../keys";
13+
import {
14+
type NodeAccounts,
15+
generateKeyForNode as _generateKeyForNode,
16+
} from "../keys";
1317
import { ChainSpec } from "../types";
1418
import { Node } from "../sharedTypes";
1519

@@ -96,23 +100,42 @@ async function clearAuthorities(specPath: string) {
96100
writeChainSpec(specPath, chainSpec);
97101
}
98102

99-
async function generateKeyForNode(nodeName?: string): Promise<any> {
100-
const keys = await _generateKeyForNode(nodeName);
103+
async function generateKeyForNode(
104+
nodeName?: string,
105+
overrideEthKey?: string,
106+
): Promise<NodeAccounts> {
107+
const keys = await _generateKeyForNode(nodeName, overrideEthKey);
101108

102109
await cryptoWaitReady();
103110

104111
const eth_keyring = new Keyring({ type: "ethereum" });
105-
const eth_account = eth_keyring.createFromUri(
106-
nodeName && nodeName.toLocaleLowerCase() in KNOWN_MOONBEAM_KEYS
107-
? KNOWN_MOONBEAM_KEYS[nodeName.toLocaleLowerCase()]
108-
: `${keys.mnemonic}/m/44'/60'/0'/0/0`,
109-
);
112+
const lowerName = nodeName?.toLocaleLowerCase();
113+
const knownKey = lowerName ? KNOWN_MOONBEAM_KEYS[lowerName] : undefined;
114+
const uri = knownKey
115+
? knownKey
116+
: overrideEthKey
117+
? overrideEthKey
118+
: `${keys.mnemonic ?? ""}/m/44'/60'/0'/0/0`;
119+
120+
let eth_account: KeyringPair;
121+
try {
122+
eth_account = eth_keyring.createFromUri(uri);
123+
} catch (error) {
124+
throw new Error(
125+
`Failed to create ethereum session key for ${nodeName ?? "collator"}: ${error}`,
126+
);
127+
}
110128

111129
keys.eth_account = {
112130
address: eth_account.address,
113131
publicKey: u8aToHex(eth_account.publicKey),
114132
};
115133

134+
if (overrideEthKey) {
135+
keys.ethKeyOverrideUsed = true;
136+
delete keys.mnemonic;
137+
}
138+
116139
return keys;
117140
}
118141

javascript/packages/orchestrator/src/configGenerator.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,10 @@ async function getCollatorNodeFromConfig(
616616

617617
const collatorName = getUniqueName(collatorConfig.name || "collator");
618618
const [decoratedKeysGenerator] = decorate(para, [generateKeyForNode]);
619-
const accountsForNode = await decoratedKeysGenerator(collatorName);
619+
const accountsForNode = await decoratedKeysGenerator(
620+
collatorName,
621+
collatorConfig.override_eth_key,
622+
);
620623

621624
const provider = networkSpec.settings.provider;
622625
const ports = await getPorts(provider, collatorConfig);

javascript/packages/orchestrator/src/keys.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Keyring } from "@polkadot/api";
2+
import type { KeyringPair } from "@polkadot/keyring/types";
23
import { u8aToHex } from "@polkadot/util";
34
import {
45
cryptoWaitReady,
@@ -13,14 +14,35 @@ function nameCase(string: string) {
1314
return string.charAt(0).toUpperCase() + string.slice(1);
1415
}
1516

16-
export async function generateKeyFromSeed(seed: string): Promise<any> {
17+
export interface SigningAccount {
18+
address: string;
19+
publicKey: string;
20+
}
21+
22+
export interface NodeAccounts {
23+
seed?: string;
24+
mnemonic?: string;
25+
sr_account: SigningAccount;
26+
sr_stash: SigningAccount;
27+
ed_account: SigningAccount;
28+
ec_account: {
29+
publicKey: string;
30+
};
31+
eth_account?: SigningAccount;
32+
ethKeyOverrideUsed?: boolean;
33+
}
34+
35+
export async function generateKeyFromSeed(seed: string): Promise<KeyringPair> {
1736
await cryptoWaitReady();
1837

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

23-
export async function generateKeyForNode(nodeName?: string): Promise<any> {
42+
export async function generateKeyForNode(
43+
nodeName?: string,
44+
_overrideEthKey?: string,
45+
): Promise<NodeAccounts> {
2446
await cryptoWaitReady();
2547

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

41-
// return the needed info
4263
return {
4364
seed,
4465
mnemonic,

javascript/packages/orchestrator/src/sharedTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export interface NodeConfig extends NodeCommonTypes {
115115
prometheus_port?: number;
116116
p2p_cert_hash?: string; // libp2p certhash to use with webrtc transport.
117117
delay_network_settings?: DelayNetworkSettings;
118+
override_eth_key?: string;
118119
}
119120

120121
export interface NodeCommonTypes {

javascript/packages/orchestrator/src/spawner.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ export const spawnNode = async (
9696
isAssetHubPolkadot,
9797
);
9898
keystoreLocalDir = path.dirname(keystoreFiles[0]);
99+
100+
// When we have `override_eth_key` option
101+
// seeds/mnemonics that should not leak into `zombie.json`;
102+
if (node.accounts.ethKeyOverrideUsed) {
103+
delete node.accounts.seed;
104+
delete node.accounts.mnemonic;
105+
delete node.accounts.ethKeyOverrideUsed;
106+
}
99107
}
100108

101109
// replace all network references in command

0 commit comments

Comments
 (0)