@@ -21,12 +21,14 @@ import { AccountId } from "@polkadot/types/interfaces"
2121import { PalletStakingRewardDestination } from "@polkadot/types/lookup"
2222import { Vec , U8 , StorageKey , Option } from "@polkadot/types/"
2323import { 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.
2829export 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+
157268export 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}
0 commit comments