feat: validator info deserialization (#403)
This commit is contained in:
committed by
Michael Vines
parent
2072f20997
commit
6f05930076
@ -142,6 +142,22 @@ declare module '@solana/web3.js' {
|
|||||||
static assign(from: PublicKey, programId: PublicKey): Transaction;
|
static assign(from: PublicKey, programId: PublicKey): Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === src/validator-info.js ===
|
||||||
|
declare export type Info = {|
|
||||||
|
name: string,
|
||||||
|
website?: string,
|
||||||
|
details?: string,
|
||||||
|
keybaseId?: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
declare export class ValidatorInfo {
|
||||||
|
key: PublicKey;
|
||||||
|
info: Info;
|
||||||
|
|
||||||
|
constructor(key: PublicKey, info: Info): ValidatorInfo;
|
||||||
|
static fromConfigData(buffer: Buffer): ?ValidatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
// === src/transaction.js ===
|
// === src/transaction.js ===
|
||||||
declare export type TransactionSignature = string;
|
declare export type TransactionSignature = string;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ export {PublicKey} from './publickey';
|
|||||||
export {SystemProgram} from './system-program';
|
export {SystemProgram} from './system-program';
|
||||||
export {Token, TokenAmount} from './token-program';
|
export {Token, TokenAmount} from './token-program';
|
||||||
export {Transaction, TransactionInstruction} from './transaction';
|
export {Transaction, TransactionInstruction} from './transaction';
|
||||||
|
export {ValidatorInfo} from './validator-info';
|
||||||
export {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
export {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
||||||
export {
|
export {
|
||||||
sendAndConfirmRawTransaction,
|
sendAndConfirmRawTransaction,
|
||||||
|
101
web3.js/src/validator-info.js
Normal file
101
web3.js/src/validator-info.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import {struct} from 'superstruct';
|
||||||
|
|
||||||
|
import * as Layout from './layout';
|
||||||
|
import * as shortvec from './util/shortvec-encoding';
|
||||||
|
import {PublicKey} from './publickey';
|
||||||
|
|
||||||
|
const VALIDATOR_INFO_KEY = new PublicKey(
|
||||||
|
'Va1idator1nfo111111111111111111111111111111',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
type ConfigKey = {|
|
||||||
|
publicKey: PublicKey,
|
||||||
|
isSigner: boolean,
|
||||||
|
|};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info used to identity validators.
|
||||||
|
*
|
||||||
|
* @typedef {Object} Info
|
||||||
|
* @property {string} name validator name
|
||||||
|
* @property {?string} website optional, validator website
|
||||||
|
* @property {?string} details optional, extra information the validator chose to share
|
||||||
|
* @property {?string} keybaseId optional, used to identify validators on keybase.io
|
||||||
|
*/
|
||||||
|
export type Info = {|
|
||||||
|
name: string,
|
||||||
|
website?: string,
|
||||||
|
details?: string,
|
||||||
|
keybaseId?: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
const InfoString = struct({
|
||||||
|
name: 'string',
|
||||||
|
website: 'string?',
|
||||||
|
details: 'string?',
|
||||||
|
keybaseId: 'string?',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValidatorInfo class
|
||||||
|
*/
|
||||||
|
export class ValidatorInfo {
|
||||||
|
/**
|
||||||
|
* validator public key
|
||||||
|
*/
|
||||||
|
key: PublicKey;
|
||||||
|
/**
|
||||||
|
* validator information
|
||||||
|
*/
|
||||||
|
info: Info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a valid ValidatorInfo
|
||||||
|
*
|
||||||
|
* @param key validator public key
|
||||||
|
* @param info validator information
|
||||||
|
*/
|
||||||
|
constructor(key: PublicKey, info: Info) {
|
||||||
|
this.key = key;
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize ValidatorInfo from the config account data. Exactly two config
|
||||||
|
* keys are required in the data.
|
||||||
|
*
|
||||||
|
* @param buffer config account data
|
||||||
|
* @return null if info was not found
|
||||||
|
*/
|
||||||
|
static fromConfigData(buffer: Buffer): ?ValidatorInfo {
|
||||||
|
const PUBKEY_LENGTH = 32;
|
||||||
|
|
||||||
|
let byteArray = [...buffer];
|
||||||
|
const configKeyCount = shortvec.decodeLength(byteArray);
|
||||||
|
if (configKeyCount !== 2) return null;
|
||||||
|
|
||||||
|
const configKeys: Array<ConfigKey> = [];
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
const publicKey = new PublicKey(byteArray.slice(0, PUBKEY_LENGTH));
|
||||||
|
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
||||||
|
const isSigner = byteArray.slice(0, 1)[0] === 1;
|
||||||
|
byteArray = byteArray.slice(1);
|
||||||
|
configKeys.push({publicKey, isSigner});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configKeys[0].publicKey.equals(VALIDATOR_INFO_KEY)) {
|
||||||
|
if (configKeys[1].isSigner) {
|
||||||
|
const rawInfo = Layout.rustString().decode(Buffer.from(byteArray));
|
||||||
|
const info = InfoString(JSON.parse(rawInfo));
|
||||||
|
return new ValidatorInfo(configKeys[1].publicKey, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
36
web3.js/test/validator-info.test.js
Normal file
36
web3.js/test/validator-info.test.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// @flow
|
||||||
|
import nacl from 'tweetnacl';
|
||||||
|
|
||||||
|
import {PublicKey} from '../src/publickey';
|
||||||
|
import {ValidatorInfo} from '../src/validator-info';
|
||||||
|
|
||||||
|
test('from config account data', () => {
|
||||||
|
const keypair = nacl.sign.keyPair.fromSeed(
|
||||||
|
Uint8Array.from(Array(32).fill(8)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedValidatorInfo = new ValidatorInfo(
|
||||||
|
new PublicKey(keypair.publicKey),
|
||||||
|
{
|
||||||
|
name: 'Validator',
|
||||||
|
keybaseId: 'validator_id',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Config data string steps:
|
||||||
|
// 1) Generate a keypair
|
||||||
|
// 2) Airdrop lamports to the account
|
||||||
|
// 3) Modify the `solana-validator-info` tool
|
||||||
|
// a) Remove the keybase id verification step
|
||||||
|
// b) Print base64 account data in the `get --all` codepath
|
||||||
|
// c) Add `println!("Account data: {:?}", base64::encode(&account.data));`
|
||||||
|
// 4) Use modified `solana-validator-info` tool to publish validator info
|
||||||
|
// 5) And then use it again to fetch the data! (feel free to trim some A's)
|
||||||
|
const configData = Buffer.from(
|
||||||
|
'AgdRlwF0SPKsXcI8nrx6x4wKJyV6xhRFjeCk8W+AAAAAABOY9ixtGkV8UbpqS189vS9p/KkyFiGNyJl+QWvRfZPKAS8AAAAAAAAAeyJrZXliYXNlSWQiOiJ2YWxpZGF0b3JfaWQiLCJuYW1lIjoiVmFsaWRhdG9yIn0',
|
||||||
|
'base64',
|
||||||
|
);
|
||||||
|
const info = ValidatorInfo.fromConfigData(configData);
|
||||||
|
|
||||||
|
expect(info).toEqual(expectedValidatorInfo);
|
||||||
|
});
|
Reference in New Issue
Block a user