-
-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: keymanager API to create signed voluntary exit message
- Loading branch information
Showing
5 changed files
with
191 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import path from "node:path"; | ||
import {expect} from "chai"; | ||
import {rimraf} from "rimraf"; | ||
import {ApiError, getClient, routes} from "@lodestar/api"; | ||
import {Api as KeymanagerApi, getClient as getKeymanagerClient} from "@lodestar/api/keymanager"; | ||
import {config} from "@lodestar/config/default"; | ||
import {interopSecretKey} from "@lodestar/state-transition"; | ||
import {gracefullyStopChildProcess, spawnCliCommand} from "@lodestar/test-utils"; | ||
import {phase0} from "@lodestar/types"; | ||
import {retry} from "@lodestar/utils"; | ||
import {testFilesDir} from "../utils.js"; | ||
|
||
describe("sign voluntary exit from api", function () { | ||
this.timeout("60s"); | ||
|
||
const dataDir = path.join(testFilesDir, "sign-voluntary-exit-test"); | ||
|
||
before("clean dataDir", () => { | ||
rimraf.sync(dataDir); | ||
}); | ||
|
||
let beaconClient: routes.beacon.Api; | ||
let keymanagerClient: KeymanagerApi; | ||
|
||
let stopDevProc = async (): Promise<void> => {}; | ||
|
||
before("start dev node with keymanager", async () => { | ||
const keymanagerPort = 38012; | ||
const beaconPort = 39012; | ||
|
||
const devProc = await spawnCliCommand( | ||
"packages/cli/bin/lodestar.js", | ||
[ | ||
// ⏎ | ||
"dev", | ||
`--dataDir=${dataDir}`, | ||
"--genesisValidators=8", | ||
"--startValidators=0..7", | ||
"--rest", | ||
`--rest.port=${beaconPort}`, | ||
// Speed up test to make genesis happen faster | ||
"--params.SECONDS_PER_SLOT=2", | ||
// Allow voluntary exists to be valid immediately | ||
"--params.SHARD_COMMITTEE_PERIOD=0", | ||
// Enable keymanager API | ||
"--keymanager", | ||
`--keymanager.port=${keymanagerPort}`, | ||
// Disable bearer token auth to simplify testing | ||
"--keymanager.authEnabled=false", | ||
], | ||
{pipeStdioToParent: false, logPrefix: "dev"} | ||
); | ||
|
||
// Exit early if process exits | ||
devProc.on("exit", (code) => { | ||
if (code !== null && code > 0) { | ||
throw new Error(`devProc process exited with code ${code}`); | ||
} | ||
}); | ||
|
||
// To cleanup the event stream connection | ||
const httpClientController = new AbortController(); | ||
|
||
beaconClient = getClient( | ||
{baseUrl: `http://127.0.0.1:${beaconPort}`, getAbortSignal: () => httpClientController.signal}, | ||
{config} | ||
).beacon; | ||
|
||
keymanagerClient = getKeymanagerClient( | ||
{baseUrl: `http://127.0.0.1:${keymanagerPort}`, getAbortSignal: () => httpClientController.signal}, | ||
{config} | ||
); | ||
|
||
// Wait for beacon node API to be available + genesis | ||
await retry( | ||
async () => { | ||
const head = await beaconClient.getBlockHeader("head"); | ||
ApiError.assert(head); | ||
if (head.response.data.header.message.slot < 1) throw Error("pre-genesis"); | ||
}, | ||
{retryDelay: 1000, retries: 20} | ||
); | ||
|
||
stopDevProc = async (): Promise<void> => { | ||
devProc.removeAllListeners("exit"); | ||
httpClientController.abort(); | ||
await gracefullyStopChildProcess(devProc, 3000); | ||
}; | ||
}); | ||
|
||
after(stopDevProc); | ||
|
||
const indexToExit = 0; | ||
const pubkeyToExit = interopSecretKey(indexToExit).toPublicKey().toHex(); | ||
|
||
let signedVoluntaryExit: phase0.SignedVoluntaryExit; | ||
|
||
it("1. create signed voluntary exit message from API", async () => { | ||
const res = await keymanagerClient.signVoluntaryExit(pubkeyToExit); | ||
ApiError.assert(res); | ||
signedVoluntaryExit = res.response.data; | ||
|
||
expect(signedVoluntaryExit.message.epoch).to.equal(0); | ||
expect(signedVoluntaryExit.message.validatorIndex).to.equal(indexToExit); | ||
expect(signedVoluntaryExit.signature).to.not.be.undefined; | ||
}); | ||
|
||
it("2. submit signed voluntary exit message to beacon node", async () => { | ||
ApiError.assert(await beaconClient.submitPoolVoluntaryExit(signedVoluntaryExit)); | ||
}); | ||
|
||
it("3. confirm validator exited as expected", async () => { | ||
await retry( | ||
async () => { | ||
const res = await beaconClient.getStateValidator("head", pubkeyToExit); | ||
ApiError.assert(res); | ||
if (res.response.data.status !== "active_exiting") { | ||
throw Error("Validator not exiting"); | ||
} else { | ||
// eslint-disable-next-line no-console | ||
console.log(`Confirmed validator ${pubkeyToExit} = ${res.response.data.status}`); | ||
} | ||
}, | ||
{retryDelay: 1000, retries: 20} | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters