Skip to content

Commit

Permalink
cloud_functions: add getReobserveVaas
Browse files Browse the repository at this point in the history
  • Loading branch information
panoel committed Oct 26, 2023
1 parent 5215f38 commit 48f3895
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 6 deletions.
2 changes: 1 addition & 1 deletion cloud_functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"scripts": {
"build": "tsc",
"dev": "ts-node src/index.ts",
"start": "npx functions-framework --target=computeTvlTvm [--signature-type=http]",
"start": "npx functions-framework --target=alarmMissingVaas [--signature-type=http]",
"deploy": "bash scripts/deploy.sh",
"gcp-build": "npm i ./dist/src/wormhole-foundation-wormhole-monitor-common-0.0.1.tgz ./dist/src/wormhole-foundation-wormhole-monitor-database-0.0.1.tgz"
},
Expand Down
1 change: 1 addition & 0 deletions cloud_functions/scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,4 @@ gcloud functions deploy tvl-history --entry-point getTVLHistory --runtime nodejs
gcloud functions deploy message-count-history --entry-point getMessageCountHistory --runtime nodejs16 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars FIRESTORE_MESSAGE_COUNT_HISTORY_COLLECTION=$FIRESTORE_MESSAGE_COUNT_HISTORY_COLLECTION
gcloud functions deploy compute-message-count-history --entry-point computeMessageCountHistory --runtime nodejs16 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 1GB --region europe-west3 --set-env-vars BIGTABLE_INSTANCE_ID=$BIGTABLE_INSTANCE_ID,BIGTABLE_SIGNED_VAAS_TABLE_ID=$BIGTABLE_SIGNED_VAAS_TABLE_ID,FIRESTORE_MESSAGE_COUNT_HISTORY_COLLECTION=$FIRESTORE_MESSAGE_COUNT_HISTORY_COLLECTION
gcloud functions deploy update-token-metadata --entry-point updateTokenMetadata --runtime nodejs16 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE
gcloud functions deploy reobserve-vaas --entry-point getReobserveVaas --runtime nodejs16 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars FIRESTORE_ALARM_MISSING_VAAS_COLLECTION=$FIRESTORE_ALARM_MISSING_VAAS_COLLECTION,REOBSERVE_VAA_API_KEY=$REOBSERVE_VAA_API_KEY
56 changes: 51 additions & 5 deletions cloud_functions/src/alarmMissingVaas.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { CHAIN_ID_TO_NAME, ChainId, ChainName } from '@certusone/wormhole-sdk';
import { MissingVaasByChain, commonGetMissingVaas } from './getMissingVaas';
import { assertEnvironmentVariable, formatAndSendToSlack } from './utils';
import { ObservedMessage } from './types';
import { assertEnvironmentVariable, formatAndSendToSlack, isVAASigned } from './utils';
import { ObservedMessage, ReobserveInfo } from './types';
import { explorerBlock, explorerTx } from '@wormhole-foundation/wormhole-monitor-common';
import { Firestore } from 'firebase-admin/firestore';
import axios from 'axios';

interface EnqueuedVAAResponse {
sequence: string;
Expand Down Expand Up @@ -45,6 +46,7 @@ export async function alarmMissingVaas(req: any, res: any) {
return;
}
let firestoreVAAs: FirestoreVAA[] = [];
let reobsMap: Map<string, ReobserveInfo> = new Map<string, ReobserveInfo>();
try {
// Get the current VAAs in the firestore holding area that we want to keep there.
// The key is the vaaKey
Expand All @@ -54,6 +56,7 @@ export async function alarmMissingVaas(req: any, res: any) {
firestoreMap.forEach((vaa) => {
firestoreVAAs.push(vaa);
});
reobsMap = await getAndProcessReobsVAAs();

// Get governed VAAS
const governedVAAs: GovernedVAAMap = await getGovernedVaas();
Expand Down Expand Up @@ -99,9 +102,18 @@ export async function alarmMissingVaas(req: any, res: any) {
console.log(`skipping over ${vaaKey} because it is governed`);
continue;
}
if (await isVAASigned(vaaKey)) {
console.log(`skipping over ${vaaKey} because it is signed`);
continue;
}
let firestoreMsg: FirestoreVAA = convert(msg);
firestoreMap.set(vaaKey, firestoreMsg);
firestoreVAAs.push(firestoreMsg);
reobsMap.set(msg.txHash, {
chain: msg.chain,
txhash: msg.txHash,
vaaKey: vaaKey,
});
await formatAndSendToSlack(formatMessage(msg));
}
}
Expand All @@ -114,7 +126,11 @@ export async function alarmMissingVaas(req: any, res: any) {
console.log('could not get missing VAAs', e);
res.sendStatus(500);
}
await updateFirestore(firestoreVAAs);
let reobs: ReobserveInfo[] = [];
reobsMap.forEach((vaa) => {
reobs.push(vaa);
});
await updateFirestore(firestoreVAAs, reobs);
res.status(200).send('successfully alarmed missing VAAS');
return;
}
Expand Down Expand Up @@ -199,13 +215,43 @@ async function getAndProcessFirestore(): Promise<Map<string, FirestoreVAA>> {
return current;
}

async function updateFirestore(vaas: FirestoreVAA[]): Promise<void> {
async function getAndProcessReobsVAAs(): Promise<Map<string, ReobserveInfo>> {
// Get VAAs in the firestore holding area.
const firestore = new Firestore();
const collection = firestore.collection(
assertEnvironmentVariable('FIRESTORE_ALARM_MISSING_VAAS_COLLECTION')
);
let current = new Map<string, ReobserveInfo>();
await collection
.doc('Reobserve')
.get()
.then((doc) => {
if (doc.exists) {
const data = doc.data();
if (data) {
const vaas: ReobserveInfo[] = data.VAAs;
vaas.forEach((vaa) => {
current.set(vaa.txhash, vaa);
});
console.log('number of reobserved VAAs', vaas.length);
}
}
})
.catch((error) => {
console.error('Error getting Reobserve document:', error);
});
return current;
}

async function updateFirestore(missing: FirestoreVAA[], reobserv: ReobserveInfo[]): Promise<void> {
const firestore = new Firestore();
const collection = firestore.collection(
assertEnvironmentVariable('FIRESTORE_ALARM_MISSING_VAAS_COLLECTION')
);
const doc = collection.doc('VAAs');
await doc.set({ VAAs: vaas });
await doc.set({ VAAs: missing });
const reobserveDoc = collection.doc('Reobserve');
await reobserveDoc.set({ VAAs: reobserv });
}

function convert(msg: ObservedMessage): FirestoreVAA {
Expand Down
76 changes: 76 additions & 0 deletions cloud_functions/src/getReobserveVaas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { assertEnvironmentVariable, isVAASigned } from './utils';
import { ReobserveInfo } from './types';
import { Firestore } from 'firebase-admin/firestore';
import axios from 'axios';

const MAX_VAAS_TO_REOBSERVE = 25;

export async function getReobserveVaas(req: any, res: any) {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return;
}
const API_KEY = assertEnvironmentVariable('REOBSERVE_VAA_API_KEY');
if (!req || !req.body || !req.body.apiKey || req.body.apiKey !== API_KEY) {
res.status(400).send('Missing or Invalid API key');
return;
}
let reobsMap: Map<string, ReobserveInfo> = new Map<string, ReobserveInfo>();
try {
reobsMap = await getAndProcessReobsVAAs();
} catch (e) {
console.log('could not get missing VAAs', e);
res.sendStatus(500);
}
let reobs: ReobserveInfo[] = [];
reobsMap.forEach((vaa) => {
reobs.push(vaa);
});
res.status(200).send(JSON.stringify(reobs));
return;
}

async function getAndProcessReobsVAAs(): Promise<Map<string, ReobserveInfo>> {
console.log('getAndProcessReobsVAAs');
// Get VAAs in the firestore holding area.
const firestore = new Firestore();
const collectionName = assertEnvironmentVariable('FIRESTORE_ALARM_MISSING_VAAS_COLLECTION');
const collectionRef = firestore.collection(collectionName).doc('Reobserve');
let current = new Map<string, ReobserveInfo>();
let putBack: ReobserveInfo[] = [];
let vaas: ReobserveInfo[] = [];

try {
const res = await firestore.runTransaction(async (t) => {
const doc = await t.get(collectionRef);
if (!doc.exists) {
console.log('Reobserve document does not exist!');
return current;
}
const data = doc.data();
if (data) {
if (data.VAAs.length > MAX_VAAS_TO_REOBSERVE) {
putBack = data.VAAs.slice(MAX_VAAS_TO_REOBSERVE);
}
vaas = data.VAAs.slice(0, MAX_VAAS_TO_REOBSERVE);
console.log('number of reobserved VAAs', vaas.length);
}
t.update(collectionRef, { VAAs: putBack });
});
} catch (e) {
console.error('error getting reobserved VAAs', e);
return current;
}
for (const vaa of vaas) {
if (!(await isVAASigned(vaa.vaaKey))) {
current.set(vaa.txhash, vaa);
}
}
console.log('number of reobservable VAAs that are not signed', current.size);
return current;
}
2 changes: 2 additions & 0 deletions cloud_functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const { getMessageCountHistory } = require('./getMessageCountHistory');
export const { computeMessageCountHistory } = require('./computeMessageCountHistory');
export const { computeTvlTvm } = require('./computeTvlTvm');
export const { updateTokenMetadata } = require('./updateTokenMetadata');
export const { getReobserveVaas } = require('./getReobserveVaas');

// Register an HTTP function with the Functions Framework that will be executed
// when you make an HTTP request to the deployed function's endpoint.
Expand All @@ -43,3 +44,4 @@ functions.http('getMessageCountHistory', getMessageCountHistory);
functions.http('computeMessageCountHistory', computeMessageCountHistory);
functions.http('computeTvlTvm', computeTvlTvm);
functions.http('updateTokenMetadata', updateTokenMetadata);
functions.http('getReobserveVaas', getReobserveVaas);
6 changes: 6 additions & 0 deletions cloud_functions/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ export type AccountEntry = {
balance: string;
};

export type ReobserveInfo = {
chain: number;
txhash: string;
vaaKey: string;
};

export type TokenMetaDatum = {
token_chain: number; //5;
token_address: string; //'000000000000000000000000df7837de1f2fa4631d716cf2502f8b230f1dcc32';
Expand Down
21 changes: 21 additions & 0 deletions cloud_functions/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const padUint16 = (s: string): string => s.padStart(MAX_UINT_16.length, '
const MAX_UINT_64 = '18446744073709551615';
export const padUint64 = (s: string): string => s.padStart(MAX_UINT_64.length, '0');

export const WormholescanRPC: string = 'https://api.wormholescan.io/';

export function parseMessageId(id: string): {
chain: number;
block: number;
Expand Down Expand Up @@ -89,3 +91,22 @@ export async function formatAndSendToSlack(msg: string): Promise<any> {
const responseData = response.data.data;
return responseData;
}

export async function isVAASigned(vaaKey: string): Promise<boolean> {
const url: string = WormholescanRPC + 'v1/signed_vaa/1/' + vaaKey;
try {
const response = await axios.get(url);
// curl -X 'GET' \
// 'https://api.wormholescan.io/v1/signed_vaa/1/ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5/319118' \
// -H 'accept: application/json'
// This function will return true if the get returns 200
// Otherwise, it will return false
if (response.status === 200) {
return true;
}
} catch (e) {
console.error('Failed to query wormholescan with url', +url + "'", e);
return false;
}
return false;
}

0 comments on commit 48f3895

Please sign in to comment.