solana/web3.js/src/vote-account.ts

237 lines
5.7 KiB
TypeScript
Raw Normal View History

import * as BufferLayout from '@solana/buffer-layout';
import type {Buffer} from 'buffer';
2019-07-23 18:06:55 -07:00
import * as Layout from './layout';
import {PublicKey} from './publickey';
import {toBuffer} from './util/to-buffer';
2019-07-23 18:06:55 -07:00
export const VOTE_PROGRAM_ID = new PublicKey(
2019-07-24 16:01:07 -07:00
'Vote111111111111111111111111111111111111111',
);
2021-03-14 20:01:35 -07:00
export type Lockout = {
slot: number;
confirmationCount: number;
};
2019-07-23 18:06:55 -07:00
/**
* History of how many credits earned by the end of each epoch
*/
export type EpochCredits = Readonly<{
2021-03-14 20:01:35 -07:00
epoch: number;
credits: number;
prevCredits: number;
}>;
2019-07-23 18:06:55 -07:00
export type AuthorizedVoter = Readonly<{
epoch: number;
authorizedVoter: PublicKey;
}>;
type AuthorizedVoterRaw = Readonly<{
authorizedVoter: Uint8Array;
epoch: number;
}>;
type PriorVoters = Readonly<{
buf: PriorVoterRaw[];
idx: number;
isEmpty: number;
}>;
export type PriorVoter = Readonly<{
authorizedPubkey: PublicKey;
epochOfLastAuthorizedSwitch: number;
targetEpoch: number;
}>;
type PriorVoterRaw = Readonly<{
authorizedPubkey: Uint8Array;
epochOfLastAuthorizedSwitch: number;
targetEpoch: number;
}>;
export type BlockTimestamp = Readonly<{
slot: number;
timestamp: number;
}>;
type VoteAccountData = Readonly<{
authorizedVoters: AuthorizedVoterRaw[];
authorizedWithdrawer: Uint8Array;
commission: number;
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
nodePubkey: Uint8Array;
priorVoters: PriorVoters;
rootSlot: number;
rootSlotValid: number;
votes: Lockout[];
}>;
2019-07-23 18:06:55 -07:00
/**
* See https://github.com/solana-labs/solana/blob/8a12ed029cfa38d4a45400916c2463fb82bbec8c/programs/vote_api/src/vote_state.rs#L68-L88
*
2021-03-14 20:01:35 -07:00
* @internal
2019-07-23 18:06:55 -07:00
*/
const VoteAccountLayout = BufferLayout.struct<VoteAccountData>([
2019-09-26 15:00:32 -07:00
Layout.publicKey('nodePubkey'),
Layout.publicKey('authorizedWithdrawer'),
2019-09-26 15:00:32 -07:00
BufferLayout.u8('commission'),
2019-07-23 18:06:55 -07:00
BufferLayout.nu64(), // votes.length
BufferLayout.seq<Lockout>(
2019-07-23 18:06:55 -07:00
BufferLayout.struct([
BufferLayout.nu64('slot'),
BufferLayout.u32('confirmationCount'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'votes',
),
BufferLayout.u8('rootSlotValid'),
BufferLayout.nu64('rootSlot'),
BufferLayout.nu64(), // authorizedVoters.length
BufferLayout.seq<AuthorizedVoterRaw>(
BufferLayout.struct([
BufferLayout.nu64('epoch'),
Layout.publicKey('authorizedVoter'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'authorizedVoters',
),
BufferLayout.struct<PriorVoters>(
[
BufferLayout.seq(
BufferLayout.struct([
Layout.publicKey('authorizedPubkey'),
BufferLayout.nu64('epochOfLastAuthorizedSwitch'),
BufferLayout.nu64('targetEpoch'),
]),
32,
'buf',
),
BufferLayout.nu64('idx'),
BufferLayout.u8('isEmpty'),
],
'priorVoters',
),
2019-07-23 18:06:55 -07:00
BufferLayout.nu64(), // epochCredits.length
BufferLayout.seq<EpochCredits>(
2019-07-23 18:06:55 -07:00
BufferLayout.struct([
BufferLayout.nu64('epoch'),
BufferLayout.nu64('credits'),
BufferLayout.nu64('prevCredits'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'epochCredits',
),
BufferLayout.struct<BlockTimestamp>(
[BufferLayout.nu64('slot'), BufferLayout.nu64('timestamp')],
'lastTimestamp',
),
2019-07-23 18:06:55 -07:00
]);
2021-03-14 20:01:35 -07:00
type VoteAccountArgs = {
nodePubkey: PublicKey;
authorizedWithdrawer: PublicKey;
2021-03-14 20:01:35 -07:00
commission: number;
rootSlot: number | null;
votes: Lockout[];
authorizedVoters: AuthorizedVoter[];
priorVoters: PriorVoter[];
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
2021-03-14 20:01:35 -07:00
};
2019-07-23 18:06:55 -07:00
/**
* VoteAccount class
*/
export class VoteAccount {
nodePubkey: PublicKey;
authorizedWithdrawer: PublicKey;
2019-07-23 18:06:55 -07:00
commission: number;
rootSlot: number | null;
votes: Lockout[];
authorizedVoters: AuthorizedVoter[];
priorVoters: PriorVoter[];
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
2019-07-23 18:06:55 -07:00
2021-03-14 20:01:35 -07:00
/**
* @internal
*/
constructor(args: VoteAccountArgs) {
this.nodePubkey = args.nodePubkey;
this.authorizedWithdrawer = args.authorizedWithdrawer;
2021-03-14 20:01:35 -07:00
this.commission = args.commission;
this.rootSlot = args.rootSlot;
this.votes = args.votes;
this.authorizedVoters = args.authorizedVoters;
this.priorVoters = args.priorVoters;
2021-03-14 20:01:35 -07:00
this.epochCredits = args.epochCredits;
this.lastTimestamp = args.lastTimestamp;
2021-03-14 20:01:35 -07:00
}
2019-07-23 18:06:55 -07:00
/**
* Deserialize VoteAccount from the account data.
*
* @param buffer account data
* @return VoteAccount
*/
2020-02-14 06:33:11 -08:00
static fromAccountData(
buffer: Buffer | Uint8Array | Array<number>,
): VoteAccount {
const versionOffset = 4;
const va = VoteAccountLayout.decode(toBuffer(buffer), versionOffset);
2021-03-14 20:01:35 -07:00
let rootSlot: number | null = va.rootSlot;
2019-07-23 18:06:55 -07:00
if (!va.rootSlotValid) {
2021-03-14 20:01:35 -07:00
rootSlot = null;
2019-07-23 18:06:55 -07:00
}
2021-03-14 20:01:35 -07:00
return new VoteAccount({
nodePubkey: new PublicKey(va.nodePubkey),
authorizedWithdrawer: new PublicKey(va.authorizedWithdrawer),
2021-03-14 20:01:35 -07:00
commission: va.commission,
votes: va.votes,
rootSlot,
authorizedVoters: va.authorizedVoters.map(parseAuthorizedVoter),
priorVoters: getPriorVoters(va.priorVoters),
2021-03-14 20:01:35 -07:00
epochCredits: va.epochCredits,
lastTimestamp: va.lastTimestamp,
2021-03-14 20:01:35 -07:00
});
2019-07-23 18:06:55 -07:00
}
}
function parseAuthorizedVoter({
authorizedVoter,
epoch,
}: AuthorizedVoterRaw): AuthorizedVoter {
return {
epoch,
authorizedVoter: new PublicKey(authorizedVoter),
};
}
function parsePriorVoters({
authorizedPubkey,
epochOfLastAuthorizedSwitch,
targetEpoch,
}: PriorVoterRaw): PriorVoter {
return {
authorizedPubkey: new PublicKey(authorizedPubkey),
epochOfLastAuthorizedSwitch,
targetEpoch,
};
}
function getPriorVoters({buf, idx, isEmpty}: PriorVoters): PriorVoter[] {
if (isEmpty) {
return [];
}
return [
...buf.slice(idx + 1).map(parsePriorVoters),
...buf.slice(0, idx).map(parsePriorVoters),
];
}