SessionId extraction for 2.x (#1127)
* SessionId extraction for 2.x * Run through e2e & update
This commit is contained in:
@@ -8,7 +8,7 @@ import { DerivedStaking, DerivedUnlocking } from '../types';
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { AccountId, BlockNumber, Exposure, Keys, Option, RewardDestination, SessionKey, SessionKeys, StakingLedger, ValidatorPrefs, UnlockChunk } from '@polkadot/types';
|
||||
import { AccountId, BlockNumber, Exposure, Keys, Option, RewardDestination, SessionKey, StakingLedger, Tuple, ValidatorPrefs, Vector, UnlockChunk } from '@polkadot/types';
|
||||
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
|
||||
@@ -72,13 +72,31 @@ function redeemableSum (stakingLedger: StakingLedger | undefined, eraLength: BN,
|
||||
}, new BN(0));
|
||||
}
|
||||
|
||||
function nextSessionId (_nextKeyFor: Option<Keys | SessionKey>): AccountId | undefined {
|
||||
const nextKeyFor: SessionKeys | SessionKey | null = _nextKeyFor.unwrapOr(null);
|
||||
function unwrapSessionIds (stashId: AccountId, validatorIds: AccountId[], auraIds: AccountId[], nextKeys: Option<SessionKey> | Vector<Tuple>): { nextSessionId?: AccountId; sessionId?: AccountId } {
|
||||
// for 2.x we have a Vec<(ValidatorId,Keys)> of the keys
|
||||
if (Array.isArray(nextKeys)) {
|
||||
const validatorIdx = validatorIds.indexOf(stashId);
|
||||
const sessionId = auraIds[validatorIdx];
|
||||
const keys = nextKeys.find(([currentId]): boolean => currentId.eq(stashId)) as [AccountId, Keys] | undefined;
|
||||
const nextSessionId = keys
|
||||
? keys[1].ed25519
|
||||
: sessionId;
|
||||
|
||||
// For substrate 2.x, nextKeyFor is SessionKeys/Keys, for 1.x it is SessionKey
|
||||
return nextKeyFor
|
||||
? (nextKeyFor as SessionKeys).auraKey || nextKeyFor
|
||||
return {
|
||||
nextSessionId,
|
||||
sessionId
|
||||
};
|
||||
}
|
||||
|
||||
// substrate 1.x
|
||||
const nextSessionId = nextKeys.isSome
|
||||
? nextKeys.unwrap()
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
nextSessionId,
|
||||
sessionId: nextSessionId
|
||||
};
|
||||
}
|
||||
|
||||
function withStashController (api: ApiInterfaceRx, accountId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
@@ -88,32 +106,43 @@ function withStashController (api: ApiInterfaceRx, accountId: AccountId, control
|
||||
combineLatest([
|
||||
eraLength(api)(),
|
||||
bestNumber(api)(),
|
||||
// FIXME while we have 2.x and 1.x support, don't add this to .multi -
|
||||
// should be added when only 2.x
|
||||
api.query.aura && api.query.aura.authorities
|
||||
? api.query.aura.authorities<Vector<AccountId>>()
|
||||
: of([] as AccountId[]),
|
||||
api.queryMulti([
|
||||
[api.query.session.nextKeyFor, controllerId],
|
||||
api.query.session.queuedKeys
|
||||
? [api.query.session.queuedKeys]
|
||||
: [api.query.session.nextKeyFor, controllerId],
|
||||
api.query.session.validators,
|
||||
[api.query.staking.ledger, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
])
|
||||
]) as any as Observable<[BN, BlockNumber, [Option<Keys | SessionKey>, Option<StakingLedger>, [AccountId[]], RewardDestination, Exposure, [ValidatorPrefs]]]>
|
||||
]) as Observable<[BN, BlockNumber, AccountId[], [Option<SessionKey> | Vector<Tuple>, AccountId[], Option<StakingLedger>, [AccountId[]], RewardDestination, Exposure, [ValidatorPrefs]]]>
|
||||
).pipe(
|
||||
map(([eraLength, bestNumber, [nextKeyFor, _stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]]): DerivedStaking => {
|
||||
map(([eraLength, bestNumber, auraIds, [nextKeys, validatorIds, _stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]]): DerivedStaking => {
|
||||
const stakingLedger = _stakingLedger.unwrapOr(null) || undefined;
|
||||
|
||||
return {
|
||||
const { sessionId, nextSessionId } = unwrapSessionIds(stashId, validatorIds, auraIds, nextKeys);
|
||||
const result: DerivedStaking = {
|
||||
accountId,
|
||||
controllerId,
|
||||
nextSessionId: nextSessionId(nextKeyFor),
|
||||
nextSessionId,
|
||||
nominators,
|
||||
redeemable: redeemableSum(stakingLedger, eraLength, bestNumber),
|
||||
rewardDestination,
|
||||
sessionId,
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
unlocking: calculateUnlocking(stakingLedger, eraLength, bestNumber),
|
||||
validatorPrefs
|
||||
} as unknown as DerivedStaking;
|
||||
};
|
||||
|
||||
return result;
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
@@ -124,25 +153,41 @@ function withControllerLedger (api: ApiInterfaceRx, accountId: AccountId, stakin
|
||||
const stashId = stakingLedger.stash;
|
||||
|
||||
return (
|
||||
api.queryMulti([
|
||||
[api.query.session.nextKeyFor, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
]) as any as Observable<[Option<Keys | SessionKey>, [AccountId[]], RewardDestination, Exposure, [ValidatorPrefs]]>
|
||||
combineLatest([
|
||||
// FIXME while we have 2.x and 1.x support, don't add this to .multi -
|
||||
// should be added when only 2.x
|
||||
api.query.aura && api.query.aura.authorities
|
||||
? api.query.aura.authorities<Vector<AccountId>>()
|
||||
: of([] as AccountId[]),
|
||||
api.queryMulti([
|
||||
api.query.session.queuedKeys
|
||||
? [api.query.session.queuedKeys]
|
||||
: [api.query.session.nextKeyFor, controllerId],
|
||||
api.query.session.validators,
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
])
|
||||
]) as Observable<[AccountId[], [Option<SessionKey> | Vector<Tuple>, AccountId[], [AccountId[]], RewardDestination, Exposure, [ValidatorPrefs]]]>
|
||||
).pipe(
|
||||
map(([nextKeyFor, [nominators], rewardDestination, stakers, [validatorPrefs]]): DerivedStaking => ({
|
||||
accountId,
|
||||
controllerId,
|
||||
nextSessionId: nextSessionId(nextKeyFor),
|
||||
nominators,
|
||||
rewardDestination,
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
validatorPrefs
|
||||
} as unknown as DerivedStaking)),
|
||||
map(([auraIds, [nextKeys, validatorIds, [nominators], rewardDestination, stakers, [validatorPrefs]]]): DerivedStaking => {
|
||||
const { nextSessionId, sessionId } = unwrapSessionIds(stashId, validatorIds, auraIds, nextKeys);
|
||||
const result: DerivedStaking = {
|
||||
accountId,
|
||||
controllerId,
|
||||
nextSessionId,
|
||||
nominators,
|
||||
rewardDestination,
|
||||
sessionId,
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
validatorPrefs
|
||||
};
|
||||
|
||||
return result;
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
@@ -158,18 +203,16 @@ export function info (api: ApiInterfaceRx): (_accountId: Uint8Array | string) =>
|
||||
api.queryMulti([
|
||||
[api.query.staking.bonded, accountId], // try to map to controller
|
||||
[api.query.staking.ledger, accountId] // try to map to stash
|
||||
]) as any as Observable<[Option<AccountId>, Option<StakingLedger>]>
|
||||
]) as Observable<[Option<AccountId>, Option<StakingLedger>]>
|
||||
).pipe(
|
||||
switchMap(([controllerId, stakingLedger]): Observable<DerivedStaking> =>
|
||||
controllerId.isSome
|
||||
// we have a controller, so input was a stash, great
|
||||
? withStashController(api, accountId, controllerId.unwrap())
|
||||
: (
|
||||
stakingLedger.isSome
|
||||
? withControllerLedger(api, accountId, stakingLedger.unwrap())
|
||||
// dangit, this is something else, ok, we are done
|
||||
: of({ accountId } as unknown as DerivedStaking)
|
||||
)
|
||||
: stakingLedger.isSome
|
||||
? withControllerLedger(api, accountId, stakingLedger.unwrap())
|
||||
// dangit, this is something else, ok, we are done
|
||||
: of({ accountId })
|
||||
),
|
||||
drr()
|
||||
);
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface DerivedStaking {
|
||||
nominators?: AccountId[];
|
||||
redeemable?: BN;
|
||||
rewardDestination?: RewardDestination;
|
||||
sessionId?: AccountId;
|
||||
stakers?: Exposure;
|
||||
stakingLedger?: StakingLedger;
|
||||
stashId?: AccountId;
|
||||
|
||||
@@ -91,122 +91,124 @@ describe.skip('derive e2e', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all staking info (for controller)', (done): Promise<() => void> => {
|
||||
const accountId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
describe('staking', (): void => {
|
||||
it('retrieves all staking info (for controller)', (done): Promise<() => void> => {
|
||||
const accountId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
|
||||
return api.derive.staking.info(accountId, (info: DerivedStaking): void => {
|
||||
console.error(JSON.stringify(info));
|
||||
return api.derive.staking.info(accountId, (info: DerivedStaking): void => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId!.eq(accountId)).toBe(true);
|
||||
expect(info.stashId!.eq('5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY')).toBe(true);
|
||||
expect(info.stashId!.eq(info.stakingLedger!.stash)).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all staking info (for stash)', (done): Promise<() => void> => {
|
||||
const accountId = '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY';
|
||||
|
||||
return api.derive.staking.info(accountId, (info: DerivedStaking): void => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
if (!info.stashId || !info.controllerId || !info.stakingLedger) {
|
||||
done.fail(new Error('At leat one of info.stashId, info.controllerId or info.stakingLedger is undefined.'));
|
||||
return;
|
||||
}
|
||||
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId.eq('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')).toBe(true);
|
||||
expect(info.stashId.eq(accountId)).toBe(true);
|
||||
expect(info.stashId.eq(info.stakingLedger.stash)).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifies derive.staking.unlocking', (): void => {
|
||||
const UNBOND_VALUE = 1;
|
||||
const alicePair = testingPairs().alice;
|
||||
|
||||
it('unbonds dots for Alice (from Alice Stash)', (done): Promise<() => void> => {
|
||||
return api.tx.staking.unbond(UNBOND_VALUE)
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (result.status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies that derive.staking.unlocking isn\'t empty/undefined', (): Promise<() => void> => {
|
||||
return api.derive.staking.info(ALICE_STASH, (info: DerivedStaking): void => {
|
||||
expect(info.unlocking).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifies derive.staking.rewardDestination', (): void => {
|
||||
const PAYEE = 2;
|
||||
const alicePair = testingPairs().alice;
|
||||
|
||||
it('Set payee for ALICE to 2', (done): Promise<() => void> => {
|
||||
return api.tx.staking
|
||||
.setPayee(PAYEE)
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (result.status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies payee for ALICE_STASH', (done): Promise<() => void> => {
|
||||
return api.derive.staking.info(ALICE_STASH, (info: DerivedStaking): void => {
|
||||
if (!info.rewardDestination) {
|
||||
return done.fail(new Error('rewardDestination is undefined.'));
|
||||
} else {
|
||||
expect(info.rewardDestination.toString()).toBe('Controller');
|
||||
}
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId!.eq(accountId)).toBe(true);
|
||||
expect(info.stashId!.eq('5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY')).toBe(true);
|
||||
expect(info.stashId!.eq(info.stakingLedger!.stash)).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('staking.info updates itself after changing reward destination', async (done): Promise<void> => {
|
||||
const stashId = testingPairs().alice_stash.address;
|
||||
it('retrieves all staking info (for stash)', (done): Promise<() => void> => {
|
||||
const accountId = '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY';
|
||||
|
||||
// start by setting the reqred to Staked, so we have a common starting point
|
||||
await api.tx.staking
|
||||
.setPayee(new RewardDestination('Staked'))
|
||||
.signAndSend(testingPairs().alice, ({ status }): void => {
|
||||
if (status.isFinalized) {
|
||||
console.error('setPayee(Staked) isFinalized');
|
||||
}
|
||||
});
|
||||
return api.derive.staking.info(accountId, (info: DerivedStaking): void => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
let count = 0; // The # of times we got a callback response from api.derive.staking.info
|
||||
|
||||
// Subscribe to staking.info
|
||||
api.derive.staking.info(stashId, (result): void => {
|
||||
++count;
|
||||
|
||||
console.error('***', count, JSON.stringify(result));
|
||||
|
||||
if (count >= 2 && result.rewardDestination!.toString() === 'Stash') {
|
||||
done();
|
||||
if (!info.stashId || !info.controllerId || !info.stakingLedger) {
|
||||
done.fail(new Error('At least one of info.stashId, info.controllerId or info.stakingLedger is undefined.'));
|
||||
return;
|
||||
}
|
||||
}).catch(console.error);
|
||||
|
||||
// Wait a bit, and change reward destination
|
||||
setTimeout(async (): Promise<void> => {
|
||||
await api.tx.staking
|
||||
.setPayee(new RewardDestination('Stash'))
|
||||
.signAndSend(testingPairs().alice, ({ status }): void => {
|
||||
if (status.isFinalized) {
|
||||
console.error('setPayee(Stash) isFinalized');
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId.eq('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')).toBe(true);
|
||||
expect(info.stashId.eq(accountId)).toBe(true);
|
||||
expect(info.stashId.eq(info.stakingLedger.stash)).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifies derive.staking.unlocking', (): void => {
|
||||
const UNBOND_VALUE = 1;
|
||||
const alicePair = testingPairs().alice;
|
||||
|
||||
it('unbonds dots for Alice (from Alice Stash)', (done): Promise<() => void> => {
|
||||
return api.tx.staking.unbond(UNBOND_VALUE)
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (result.status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
it('verifies that derive.staking.unlocking isn\'t empty/undefined', (): Promise<() => void> => {
|
||||
return api.derive.staking.info(ALICE_STASH, (info: DerivedStaking): void => {
|
||||
expect(info.unlocking).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifies derive.staking.rewardDestination', (): void => {
|
||||
const PAYEE = 2;
|
||||
const alicePair = testingPairs().alice;
|
||||
|
||||
it('Set payee for ALICE to 2', (done): Promise<() => void> => {
|
||||
return api.tx.staking
|
||||
.setPayee(PAYEE)
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (result.status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies payee for ALICE_STASH', (done): Promise<() => void> => {
|
||||
return api.derive.staking.info(ALICE_STASH, (info: DerivedStaking): void => {
|
||||
if (!info.rewardDestination) {
|
||||
return done.fail(new Error('rewardDestination is undefined.'));
|
||||
} else {
|
||||
expect(info.rewardDestination.toString()).toBe('Controller');
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('staking.info updates itself after changing reward destination', async (done): Promise<void> => {
|
||||
const stashId = testingPairs().alice_stash.address;
|
||||
|
||||
// start by setting the reqred to Staked, so we have a common starting point
|
||||
await api.tx.staking
|
||||
.setPayee(new RewardDestination('Staked'))
|
||||
.signAndSend(testingPairs().alice, ({ status }): void => {
|
||||
if (status.isFinalized) {
|
||||
console.error('setPayee(Staked) isFinalized');
|
||||
}
|
||||
});
|
||||
|
||||
let count = 0; // The # of times we got a callback response from api.derive.staking.info
|
||||
|
||||
// Subscribe to staking.info
|
||||
api.derive.staking.info(stashId, (result): void => {
|
||||
++count;
|
||||
|
||||
console.error('***', count, JSON.stringify(result));
|
||||
|
||||
if (count >= 2 && result.rewardDestination!.toString() === 'Stash') {
|
||||
done();
|
||||
}
|
||||
}).catch(console.error);
|
||||
|
||||
// Wait a bit, and change reward destination
|
||||
setTimeout(async (): Promise<void> => {
|
||||
await api.tx.staking
|
||||
.setPayee(new RewardDestination('Stash'))
|
||||
.signAndSend(testingPairs().alice, ({ status }): void => {
|
||||
if (status.isFinalized) {
|
||||
console.error('setPayee(Stash) isFinalized');
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,22 +13,14 @@ import SessionKey from './SessionKey';
|
||||
export default class SessionKeys extends Struct {
|
||||
public constructor (value?: any) {
|
||||
super({
|
||||
grandpaKey: SessionKey,
|
||||
auraKey: SessionKey
|
||||
ed25519: SessionKey
|
||||
}, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The Aura session
|
||||
*/
|
||||
public get auraKey (): SessionKey {
|
||||
return this.get('auraKey') as SessionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The Grandpa Authority
|
||||
*/
|
||||
public get grandpaKey (): SessionKey {
|
||||
return this.get('grandpaKey') as SessionKey;
|
||||
public get ed25519 (): SessionKey {
|
||||
return this.get('ed25519') as SessionKey;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user