Skip to content
This repository has been archived by the owner on Jul 22, 2020. It is now read-only.

Commit

Permalink
fix: rename info to identity and batch user lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored and sunnygleason committed Jul 19, 2019
1 parent 72f905e commit ff24235
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 45 deletions.
78 changes: 45 additions & 33 deletions api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const GLOBAL_STATS_BROADCAST_INTERVAL_MS = 2000;
const CLUSTER_INFO_BROADCAST_INTERVAL_MS = 16000;
const CLUSTER_INFO_CACHE_TIME_SECS = 35000;
const CONFIG_PROGRAM_ID = 'Config1111111111111111111111111111111111111';
const MAX_KEYBASE_USER_LOOKUP = 50;

const app = express();

Expand Down Expand Up @@ -499,7 +500,7 @@ async function getClusterInfo() {
let [, feeCalculator] = await connection.getRecentBlockhash();
let supply = await connection.getTotalSupply();
let cluster = await connection.getClusterNodes();
let validators = await fetchValidatorInfo(cluster.map(c => c.pubkey));
let identities = await fetchValidatorIdentities(cluster.map(c => c.pubkey));
let voting = await connection.getEpochVoteAccounts();
let totalStaked = _.reduce(
voting,
Expand Down Expand Up @@ -532,7 +533,7 @@ async function getClusterInfo() {
supply,
totalStaked,
cluster,
validators,
identities,
voting,
ts,
};
Expand Down Expand Up @@ -565,13 +566,38 @@ app.get('/cluster-info', (req, res) => {
sendClusterResult(req, res);
});

async function fetchValidatorInfo(keys) {
async function fetchValidatorAvatars(keybaseUsernames) {
const avatarMap = new Map();
let batch = keybaseUsernames.splice(0, MAX_KEYBASE_USER_LOOKUP)
while (batch.length > 0) {
const usernames = batch.join(',');
const keybaseApiUrl = `https://keybase.io/_/api/1.0/user/lookup.json?usernames=${usernames}&fields=pictures,basics`;
try {
const keybaseResponse = await fetch(keybaseApiUrl);
const keybaseData = await keybaseResponse.json();
if (keybaseData && keybaseData.them) {
for (const {basics, pictures} of keybaseData.them) {
if (basics && basics.username && pictures && pictures.primary && pictures.primary.url) {
avatarMap.set(basics.username, pictures.primary.url);
}
}
}
} catch(err) {
// Skip failed batch
}
// Prepare next batch
batch = keybaseUsernames.splice(0, MAX_KEYBASE_USER_LOOKUP);
}
return avatarMap;
}

async function fetchValidatorIdentities(keys) {
const configKey = new solanaWeb3.PublicKey(CONFIG_PROGRAM_ID);
const connection = new solanaWeb3.Connection(FULLNODE_URL);
const accounts = await connection.getProgramAccounts(configKey);
const keySet = new Set(keys);

const results = await Promise.all(
let identities = await Promise.all(
accounts.map(async account => {
let validatorInfo;
try {
Expand All @@ -584,50 +610,36 @@ async function fetchValidatorInfo(keys) {
const validatorKeyStr = validatorInfo.key.toString();
if (keySet.has(validatorKeyStr)) {
keySet.delete(validatorKeyStr);

// build info and verify
const info = validatorInfo.info;
const keybaseUsername = info.keybaseUsername;
// build identity and verify
const identity = validatorInfo.info;
const keybaseUsername = identity.keybaseUsername;
if (keybaseUsername) {
const keybaseUrl = `https://keybase.pub/${keybaseUsername}/solana/validator-${validatorKeyStr}`;
const keybaseResponse = await fetch(keybaseUrl, {method: 'HEAD'});
const verified = keybaseResponse.status === 200;
info.verified = verified;
info.verifyUrl = keybaseUrl;
identity.verified = verified;
identity.verifyUrl = keybaseUrl;
}
info.pubkey = validatorKeyStr;
return info;
identity.pubkey = validatorKeyStr;
return identity;
}
}
}),
);

const infoList = results.filter(r => r);
const keybaseUsernames = infoList.map(info => info.keybaseUsername).filter(u => u).join(',');
const avatarMap = new Map();
if (keybaseUsernames.length > 0) {
const keybaseApiUrl = `https://keybase.io/_/api/1.0/user/lookup.json?usernames=${keybaseUsernames}&fields=pictures,basics`;
const keybaseResponse = await fetch(keybaseApiUrl);
const keybaseData = await keybaseResponse.json();
if (keybaseData && keybaseData.them) {
for (const {basics, pictures} of keybaseData.them) {
if (basics && basics.username && pictures && pictures.primary && pictures.primary.url) {
avatarMap.set(basics.username, pictures.primary.url);
}
}
}
}

for (const info of infoList) {
if (info.keybaseUsername) {
const avatarUrl = avatarMap.get(info.keybaseUsername);
identities = identities.filter(r => r);
const keybaseUsernames = identities.map(i => i.keybaseUsername).filter(u => u);
const avatarMap = await fetchValidatorAvatars(keybaseUsernames);
for (const identity of identities) {
if (identity.keybaseUsername) {
const avatarUrl = avatarMap.get(identity.keybaseUsername);
if (avatarUrl) {
info.avatarUrl = avatarUrl;
identity.avatarUrl = avatarUrl;
}
}
}

return infoList;
return identities;
}

app.listen(port, () => console.log(`Listening on port ${port}!`));
6 changes: 3 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,14 @@ class App extends Component {
parseClusterInfo(data) {
let voting = data.voting;
let gossip = data.cluster;
let validators = data.validators;
let identities = data.identities;

let nodes = _.map(gossip, g => {
let newG = {...g};
let vote = voting.find(x => x.nodePubkey === newG.pubkey);
newG.voteAccount = vote;
let info = validators.find(v => v.pubkey === newG.pubkey);
newG.info = info;
let identity = identities.find(v => v.pubkey === newG.pubkey);
newG.identity = identity;

return newG;
});
Expand Down
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelTourDeSolLeaderboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ class Bx2PanelTourDeSolLeaderboard extends Component {
{(row.voteAccount && row.voteAccount.stake) || 0} Lamports
</TableCell>
<TableCell align="center" style={{verticalAlign: 'middle'}}>
<BxValidatorIdentity info={row.info} />
<BxValidatorIdentity identity={row.identity} />
</TableCell>
<TableCell align="right">TODO</TableCell>
</TableRow>
Expand Down
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelValidatorDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class Bx2PanelValidatorDetail extends Component {
{(row.voteAccount && row.voteAccount.stake) || 0} Lamports
</TableCell>
<TableCell align="center" style={{verticalAlign: 'middle'}}>
<BxValidatorIdentity info={row.info} />
<BxValidatorIdentity identity={row.identity} />
</TableCell>
<TableCell align="center">TODO</TableCell>
<TableCell align="right">TODO</TableCell>
Expand Down
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelValidators.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Bx2PanelValidators extends React.Component {
{(row.voteAccount && row.voteAccount.stake) || 0} Lamports
</TableCell>
<TableCell align="center" style={{verticalAlign: 'middle'}}>
<BxValidatorIdentity info={row.info} />
<BxValidatorIdentity identity={row.identity} />
</TableCell>
<TableCell align="center">TODO</TableCell>
<TableCell align="right">TODO</TableCell>
Expand Down
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelValidatorsOverview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ class Bx2PanelValidatorsOverview extends Component {
{(row.voteAccount && row.voteAccount.stake) || 0} Lamports
</TableCell>
<TableCell align="center">
<BxValidatorIdentity info={row.info} />
<BxValidatorIdentity identity={row.identity} />
</TableCell>
<TableCell align="center">TODO</TableCell>
<TableCell align="right">TODO</TableCell>
Expand Down
10 changes: 5 additions & 5 deletions src/v2/Bx2ValidatorIdentity.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Link from '@material-ui/core/Link';
class Bx2ValidatorIdentity extends Component {

renderAvatar() {
let {avatarUrl, name} = this.props.info;
let {avatarUrl, name} = this.props.identity;

const avatarStyle = {
marginRight: 10,
Expand Down Expand Up @@ -39,7 +39,7 @@ class Bx2ValidatorIdentity extends Component {
}

renderVerified() {
const {verified, verifyUrl} = this.props.info;
const {verified, verifyUrl} = this.props.identity;

let verifiedIcon;
if (verified && verifyUrl) {
Expand Down Expand Up @@ -70,7 +70,7 @@ class Bx2ValidatorIdentity extends Component {
}

renderName() {
const {name, verified, website} = this.props.info;
const {name, verified, website} = this.props.identity;

let color = 'secondary';
if (!verified) {
Expand Down Expand Up @@ -121,7 +121,7 @@ class Bx2ValidatorIdentity extends Component {
}

render() {
if (!this.props.info) {
if (!this.props.identity) {
return this.renderMissingInfo();
}

Expand All @@ -136,7 +136,7 @@ class Bx2ValidatorIdentity extends Component {
}

Bx2ValidatorIdentity.propTypes = {
info: PropTypes.object,
identity: PropTypes.object,
};

export default Bx2ValidatorIdentity;

0 comments on commit ff24235

Please sign in to comment.