2021-07-08 18:01:11 -07:00
|
|
|
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';
|
2022-08-11 02:10:11 -07:00
|
|
|
import {toBuffer} from './utils/to-buffer';
|
2019-07-23 18:06:55 -07:00
|
|
|
|
2019-12-23 10:46:49 -08: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
|
|
|
|
*/
|
2022-03-23 23:26:19 -07:00
|
|
|
export type EpochCredits = Readonly<{
|
2021-03-14 20:01:35 -07:00
|
|
|
epoch: number;
|
|
|
|
credits: number;
|
|
|
|
prevCredits: number;
|
2022-03-23 23:26:19 -07:00
|
|
|
}>;
|
2019-07-23 18:06:55 -07:00
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
export type AuthorizedVoter = Readonly<{
|
2021-11-30 10:55:21 -08:00
|
|
|
epoch: number;
|
|
|
|
authorizedVoter: PublicKey;
|
2022-03-23 23:26:19 -07:00
|
|
|
}>;
|
2021-11-30 10:55:21 -08:00
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
type AuthorizedVoterRaw = Readonly<{
|
|
|
|
authorizedVoter: Uint8Array;
|
|
|
|
epoch: number;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
type PriorVoters = Readonly<{
|
|
|
|
buf: PriorVoterRaw[];
|
|
|
|
idx: number;
|
|
|
|
isEmpty: number;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
export type PriorVoter = Readonly<{
|
2021-11-30 10:55:21 -08:00
|
|
|
authorizedPubkey: PublicKey;
|
|
|
|
epochOfLastAuthorizedSwitch: number;
|
|
|
|
targetEpoch: number;
|
2022-03-23 23:26:19 -07:00
|
|
|
}>;
|
2021-11-30 10:55:21 -08:00
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
type PriorVoterRaw = Readonly<{
|
|
|
|
authorizedPubkey: Uint8Array;
|
|
|
|
epochOfLastAuthorizedSwitch: number;
|
|
|
|
targetEpoch: number;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
export type BlockTimestamp = Readonly<{
|
2021-11-30 10:55:21 -08:00
|
|
|
slot: number;
|
2022-03-23 23:14:41 -07:00
|
|
|
timestamp: number;
|
2022-03-23 23:26:19 -07:00
|
|
|
}>;
|
|
|
|
|
|
|
|
type VoteAccountData = Readonly<{
|
|
|
|
authorizedVoters: AuthorizedVoterRaw[];
|
|
|
|
authorizedWithdrawer: Uint8Array;
|
|
|
|
commission: number;
|
|
|
|
epochCredits: EpochCredits[];
|
|
|
|
lastTimestamp: BlockTimestamp;
|
|
|
|
nodePubkey: Uint8Array;
|
|
|
|
priorVoters: PriorVoters;
|
|
|
|
rootSlot: number;
|
|
|
|
rootSlotValid: number;
|
|
|
|
votes: Lockout[];
|
|
|
|
}>;
|
2021-11-30 10:55:21 -08:00
|
|
|
|
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
|
|
|
*/
|
2022-03-23 23:26:19 -07:00
|
|
|
const VoteAccountLayout = BufferLayout.struct<VoteAccountData>([
|
2019-09-26 15:00:32 -07:00
|
|
|
Layout.publicKey('nodePubkey'),
|
2021-11-30 10:55:21 -08:00
|
|
|
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
|
2022-03-23 23:26:19 -07:00
|
|
|
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'),
|
2021-11-30 10:55:21 -08:00
|
|
|
BufferLayout.nu64(), // authorizedVoters.length
|
2022-03-23 23:26:19 -07:00
|
|
|
BufferLayout.seq<AuthorizedVoterRaw>(
|
2021-11-30 10:55:21 -08:00
|
|
|
BufferLayout.struct([
|
|
|
|
BufferLayout.nu64('epoch'),
|
|
|
|
Layout.publicKey('authorizedVoter'),
|
|
|
|
]),
|
|
|
|
BufferLayout.offset(BufferLayout.u32(), -8),
|
|
|
|
'authorizedVoters',
|
|
|
|
),
|
2022-03-23 23:26:19 -07:00
|
|
|
BufferLayout.struct<PriorVoters>(
|
2021-11-30 10:55:21 -08:00
|
|
|
[
|
|
|
|
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
|
2022-03-23 23:26:19 -07:00
|
|
|
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',
|
|
|
|
),
|
2022-03-23 23:26:19 -07:00
|
|
|
BufferLayout.struct<BlockTimestamp>(
|
2021-11-30 10:55:21 -08:00
|
|
|
[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;
|
2021-11-30 10:55:21 -08:00
|
|
|
authorizedWithdrawer: PublicKey;
|
2021-03-14 20:01:35 -07:00
|
|
|
commission: number;
|
|
|
|
rootSlot: number | null;
|
2021-11-30 10:55:21 -08:00
|
|
|
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;
|
2021-11-30 10:55:21 -08:00
|
|
|
authorizedWithdrawer: PublicKey;
|
2019-07-23 18:06:55 -07:00
|
|
|
commission: number;
|
|
|
|
rootSlot: number | null;
|
2021-11-30 10:55:21 -08:00
|
|
|
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;
|
2021-11-30 10:55:21 -08:00
|
|
|
this.authorizedWithdrawer = args.authorizedWithdrawer;
|
2021-03-14 20:01:35 -07:00
|
|
|
this.commission = args.commission;
|
|
|
|
this.rootSlot = args.rootSlot;
|
2021-11-30 10:55:21 -08:00
|
|
|
this.votes = args.votes;
|
|
|
|
this.authorizedVoters = args.authorizedVoters;
|
|
|
|
this.priorVoters = args.priorVoters;
|
2021-03-14 20:01:35 -07:00
|
|
|
this.epochCredits = args.epochCredits;
|
2021-11-30 10:55:21 -08:00
|
|
|
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 {
|
2021-11-30 10:55:21 -08:00
|
|
|
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),
|
2021-11-30 10:55:21 -08:00
|
|
|
authorizedWithdrawer: new PublicKey(va.authorizedWithdrawer),
|
2021-03-14 20:01:35 -07:00
|
|
|
commission: va.commission,
|
|
|
|
votes: va.votes,
|
|
|
|
rootSlot,
|
2021-11-30 10:55:21 -08:00
|
|
|
authorizedVoters: va.authorizedVoters.map(parseAuthorizedVoter),
|
|
|
|
priorVoters: getPriorVoters(va.priorVoters),
|
2021-03-14 20:01:35 -07:00
|
|
|
epochCredits: va.epochCredits,
|
2021-11-30 10:55:21 -08:00
|
|
|
lastTimestamp: va.lastTimestamp,
|
2021-03-14 20:01:35 -07:00
|
|
|
});
|
2019-07-23 18:06:55 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-30 10:55:21 -08:00
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
function parseAuthorizedVoter({
|
|
|
|
authorizedVoter,
|
|
|
|
epoch,
|
|
|
|
}: AuthorizedVoterRaw): AuthorizedVoter {
|
2021-11-30 10:55:21 -08:00
|
|
|
return {
|
|
|
|
epoch,
|
|
|
|
authorizedVoter: new PublicKey(authorizedVoter),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function parsePriorVoters({
|
|
|
|
authorizedPubkey,
|
|
|
|
epochOfLastAuthorizedSwitch,
|
|
|
|
targetEpoch,
|
2022-03-23 23:26:19 -07:00
|
|
|
}: PriorVoterRaw): PriorVoter {
|
2021-11-30 10:55:21 -08:00
|
|
|
return {
|
|
|
|
authorizedPubkey: new PublicKey(authorizedPubkey),
|
|
|
|
epochOfLastAuthorizedSwitch,
|
|
|
|
targetEpoch,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
function getPriorVoters({buf, idx, isEmpty}: PriorVoters): PriorVoter[] {
|
2021-11-30 10:55:21 -08:00
|
|
|
if (isEmpty) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:26:19 -07:00
|
|
|
return [
|
|
|
|
...buf.slice(idx + 1).map(parsePriorVoters),
|
|
|
|
...buf.slice(0, idx).map(parsePriorVoters),
|
|
|
|
];
|
2021-11-30 10:55:21 -08:00
|
|
|
}
|