Skip to content

Commit b00ea2d

Browse files
committed
a bunch of new commands and scripts -- need to move this shit to bun
1 parent b56afda commit b00ea2d

File tree

4 files changed

+167
-24
lines changed

4 files changed

+167
-24
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@polkadot/api": "15.7.2",
3737
"axios": "^1.8.2",
3838
"bn.js": "^5.2.1",
39+
"stdout-update": "^4.0.1",
3940
"yargs": "^17.7.2"
4041
}
4142
}

src/handlers.ts

Lines changed: 141 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ import { AccountId } from "@polkadot/types/interfaces"
2121
import { PalletStakingRewardDestination } from "@polkadot/types/lookup"
2222
import { Vec, U8, StorageKey, Option } from "@polkadot/types/"
2323
import { signFakeWithApi, signFake } from '@acala-network/chopsticks-utils'
24-
import { sign } from 'crypto';
24+
import { IEvent, IEventData } from '@polkadot/types/types';
25+
import UpdateManager from 'stdout-update';
2526

2627

2728
/// TODO: split this per command, it is causing annoyance.
2829
export interface HandlerArgs {
2930
ws: string;
31+
ws2?: string;
3032
sendTx?: boolean;
3133
count?: number;
3234
noDryRun?: boolean;
@@ -154,6 +156,115 @@ export async function stakingStatsHandler(args: HandlerArgs): Promise<void> {
154156
// await electionScoreHandler(args);
155157
}
156158

159+
export async function commandCenterHandler(): Promise<void> {
160+
const rcApi = await getApi("ws://localhost:9955");
161+
const ahApi = await getApi("ws://localhost:9966");
162+
163+
const manager = UpdateManager.getInstance();
164+
// manager.hook();
165+
166+
let rcOutput: string[] = []
167+
let ahOutput: string[] = []
168+
const rcEvents: string[] = []
169+
const ahEvents: string[] = []
170+
171+
172+
rcApi.rpc.chain.subscribeFinalizedHeads(async (header) => {
173+
// --- RC:
174+
// current session index
175+
const index = await rcApi.query.session.currentIndex();
176+
// whether the session pallet has a queued validator set within it
177+
const hasQueuedInSession = await rcApi.query.session.queuedChanged();
178+
// the range of historical session data that we have in the RC.
179+
const historicalRange = await rcApi.query.historical.storedRange();
180+
181+
182+
// whether there is a validator set queued in ah-client. for this we need to display only the id and the length of the set.
183+
const hasQueuedInClient = await rcApi.query.stakingNextAhClient.validatorSet();
184+
// whether we have already passed a new validator set to session, and therefore in the next session rotation we want to pass this id to AH.
185+
const hasNextActiveId = await rcApi.query.stakingNextAhClient.nextSessionChangesValidators();
186+
// whether the AhClient pallet is blocked or not, useful for migration signal from the fellowship.
187+
const isBlocked = await rcApi.query.stakingNextAhClient.isBlocked();
188+
189+
// Events that we are interested in from RC:
190+
const eventsOfInterest = (await rcApi.query.system.events())
191+
.map((e) => e.event)
192+
.filter((e) => {
193+
const ahClientEvents = (e: IEventData) => e.section == 'stakingNextAhClient';
194+
const sessionEvents = (e: IEventData) => e.section == 'session' || e.section == 'historical';
195+
return ahClientEvents(e.data) || sessionEvents(e.data);
196+
})
197+
.map((e) => `${e.section.toString()}::${e.method.toString()}(${e.data.toString()})`);
198+
rcEvents.push(...eventsOfInterest);
199+
rcOutput = [
200+
`RC:`,
201+
`finalized block ${header.number}`,
202+
`RC.session: index=${index}, hasQueuedInSession=${hasQueuedInSession}, historicalRange=${historicalRange}`,
203+
`RC.stakingNextAhClient: hasQueuedInClient=${hasQueuedInClient}, hasNextActiveId=${hasNextActiveId}, isBlocked=${isBlocked}`,
204+
`RC.events: ${rcEvents}`,
205+
`----`
206+
]
207+
208+
manager.update(rcOutput.concat(ahOutput))
209+
})
210+
211+
// AH:
212+
ahApi.rpc.chain.subscribeFinalizedHeads(async (header) => {
213+
// the current planned era
214+
const currentEra = await ahApi.query.staking.currentEra();
215+
// the active era
216+
const activeEra = await ahApi.query.staking.activeEra();
217+
// the starting index of the active era
218+
const erasStartSessionIndex = await ahApi.query.staking.erasStartSessionIndex(activeEra.unwrap().index)
219+
220+
// the basic state of the election provider
221+
const phase = await ahApi.query.multiBlock.currentPhase();
222+
const round = await ahApi.query.multiBlock.round();
223+
const snapshotRange = (await ahApi.query.multiBlock.pagedVoterSnapshotHash.entries()).map(([k, v]) => k.args[0]).sort();
224+
const queuedScore = await ahApi.query.multiBlockVerifier.queuedSolutionScore();
225+
const signedSubmissions = await ahApi.query.multiBlockSigned.sortedScores(round);
226+
227+
// Events that we are interested in from RC:
228+
const eventsOfInterest = (await ahApi.query.system.events())
229+
.map((e) => e.event)
230+
.filter((e) => {
231+
const election = (e: IEventData) => e.section == 'multiBlock' || e.section == 'multiBlockVerifier' || e.section == 'multiBlockSigned' || e.section == 'multiBlockUnsigned';
232+
const rcClient = (e: IEventData) => e.section == 'stakingNextRcClient';
233+
const staking = (e: IEventData) => e.section == 'staking' && (e.method == 'EraPaid' || e.method == 'SessionRotated' || e.method == 'PagedElectionProceeded');
234+
return election(e.data) || rcClient(e.data) || staking(e.data);
235+
})
236+
.map((e) => `${e.section.toString()}::${e.method.toString()}(${e.data.toString()})`);
237+
ahEvents.push(...eventsOfInterest);
238+
239+
ahOutput = [
240+
`AH:`,
241+
`finalized block ${header.number}`,
242+
`AH.staking: currentEra=${currentEra}, activeEra=${activeEra}, erasStartSessionIndex(${activeEra.unwrap().index})=${erasStartSessionIndex}`,
243+
`multiBlock: phase=${phase}, round=${round}, snapshotRange=${snapshotRange}, queuedScore=${queuedScore}, signedSubmissions=${signedSubmissions}`,
244+
`AH.events: ${ahEvents}`,
245+
`----`,
246+
]
247+
248+
manager.update(rcOutput.concat(ahOutput))
249+
});
250+
251+
252+
// Prevent the function from returning by creating a promise that never resolves
253+
return new Promise<void>((resolve) => {
254+
// Set up signal handlers for graceful shutdown
255+
process.on('SIGINT', () => {
256+
console.log('Received SIGINT. Shutting down...');
257+
process.exit(0);
258+
});
259+
260+
process.on('SIGTERM', () => {
261+
console.log('Received SIGTERM. Shutting down...');
262+
process.exit(0);
263+
});
264+
console.log('Command center running. Press Ctrl+C to exit.');
265+
});
266+
}
267+
157268
export async function scrapePrefixKeys(prefix: string, api: ApiPromise): Promise<string[]> {
158269
let lastKey = null
159270
const keys: string[] = [];
@@ -184,31 +295,39 @@ export async function fakeSignForChopsticks(api: ApiPromise, sender: string | Ac
184295
tx.signature.set(mockSignature)
185296
}
186297

187-
export async function playgroundHandler({ ws }: HandlerArgs): Promise<void> {
298+
export async function ExposureStats({ ws }: HandlerArgs): Promise<void> {
188299
const api = await getApi(ws);
189-
const stakers = await api.query.staking.ledger.entries();
190-
const overstaked = []
191-
for (const [key, staker] of stakers) {
192-
const total = staker.unwrap().total;
193-
const stash = staker.unwrap().stash;
194-
const locked = await api.query.balances.locks(stash);
195-
const stash_account = await api.query.system.account(stash);
196-
const stash_free = stash_account.data.free;
197-
const staking_locks = locked.filter(lock => lock.id.toString().trim() == '0x7374616b696e6720');
198-
199-
if (staking_locks.length != 1) {
200-
console.log(`Staker ${stash} has ${staking_locks.length} staking locks, free: ${stash_free}`);
201-
overstaked.push({ staker: staker.unwrap(), locked: null, free: stash_free });
202-
continue
203-
}
204300

205-
const staking_lock = staking_locks[0];
301+
const era = (await api.query.staking.currentEra()).unwrap();
302+
const overviews = (await api.query.staking.erasStakersOverview.entries(era)).map(([key, value]) => {
303+
const stash = key.args[1].toHuman();
304+
const metadata = value.unwrap();
305+
return { stash, metadata }
306+
});
307+
console.log(`overviews/exposed validators: ${overviews.length}`);
308+
const sumNominators = overviews.map(({ metadata}) => metadata.nominatorCount.toNumber()).reduce((a, b) => a + b, 0);
309+
console.log(`sumNominators: ${sumNominators}`);
310+
}
311+
312+
export async function controllerStats({ ws }: HandlerArgs): Promise<void> {
313+
const api = await getApi(ws);
314+
const bonded = await api.query.staking.bonded.entries();
206315

207-
if (staking_lock.amount.toBigInt() != total.toBigInt() || stash_free.toBigInt() < total.toBigInt()) {
208-
console.log(`Stash: ${stash}, Total: ${total}, Locked: ${staking_lock.amount}, free: ${stash_free}, diff: ${(total.toBigInt() - stash_free.toBigInt()) / BigInt(10e12)}`);
209-
overstaked.push({ staker: staker, locked: staking_lock.amount, free: stash_free });
316+
let same = 0;
317+
let different = 0;
318+
for (const [key, value] of bonded) {
319+
const stash = key.args[0].toHuman();
320+
const ctrl = value.unwrap().toHuman();
321+
if (stash == ctrl) {
322+
same += 1
323+
}
324+
else {
325+
different += 1
210326
}
211327
}
328+
console.log(`bonded: same=${same}, different=${different}`)
329+
}
212330

213-
console.log(overstaked.length)
331+
export async function playgroundHandler(args: HandlerArgs): Promise<void> {
332+
await ExposureStats(args);
214333
}

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
chillOtherHandler,
1212
stateTrieMigrationHandler,
1313
stakingStatsHandler,
14-
inFrontHandler
14+
inFrontHandler,
15+
commandCenterHandler
1516
} from './handlers';
1617

1718
// Export all of the services so this codebase can be used as a library as well.
@@ -194,6 +195,8 @@ async function main() {
194195
stateTrieMigrationHandler
195196
)
196197
// @ts-ignore
198+
.command(['ahm-command-center'], 'ahm', {}, commandCenterHandler)
199+
// @ts-ignore
197200
.command(['playground'], 'random stuff', {}, playgroundHandler)
198201
.parse();
199202
}

yarn.lock

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1704,6 +1704,11 @@ ajv@^6.12.4:
17041704
json-schema-traverse "^0.4.1"
17051705
uri-js "^4.2.2"
17061706

1707+
ansi-escapes@^6.2.0:
1708+
version "6.2.1"
1709+
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.1.tgz#76c54ce9b081dad39acec4b5d53377913825fb0f"
1710+
integrity sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==
1711+
17071712
ansi-regex@^5.0.1:
17081713
version "5.0.1"
17091714
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
@@ -1726,6 +1731,11 @@ ansi-styles@^6.1.0:
17261731
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz"
17271732
integrity sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==
17281733

1734+
ansi-styles@^6.2.1:
1735+
version "6.2.1"
1736+
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
1737+
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
1738+
17291739
ansis@^3.9.0:
17301740
version "3.17.0"
17311741
resolved "https://registry.yarnpkg.com/ansis/-/ansis-3.17.0.tgz#fa8d9c2a93fe7d1177e0c17f9eeb562a58a832d7"
@@ -4391,6 +4401,16 @@ stdin-discarder@^0.2.2:
43914401
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be"
43924402
integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==
43934403

4404+
stdout-update@^4.0.1:
4405+
version "4.0.1"
4406+
resolved "https://registry.yarnpkg.com/stdout-update/-/stdout-update-4.0.1.tgz#63ce952ccd6a40de7eeb288e9c609ff70f1fa540"
4407+
integrity sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==
4408+
dependencies:
4409+
ansi-escapes "^6.2.0"
4410+
ansi-styles "^6.2.1"
4411+
string-width "^7.1.0"
4412+
strip-ansi "^7.1.0"
4413+
43944414
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
43954415
version "4.2.3"
43964416
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@@ -4409,7 +4429,7 @@ string-width@^5.0.1, string-width@^5.1.2:
44094429
emoji-regex "^9.2.2"
44104430
strip-ansi "^7.0.1"
44114431

4412-
string-width@^7.2.0:
4432+
string-width@^7.1.0, string-width@^7.2.0:
44134433
version "7.2.0"
44144434
resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc"
44154435
integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==

0 commit comments

Comments
 (0)