-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathgetReobserveVaas.ts
124 lines (116 loc) · 4.56 KB
/
getReobserveVaas.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { assertEnvironmentVariable, isVAASigned } from './utils';
import { ReobserveInfo } from './types';
import { Firestore } from 'firebase-admin/firestore';
import { CHAIN_ID_SOLANA } from '@certusone/wormhole-sdk';
import { convertSolanaTxToAccts } from '@wormhole-foundation/wormhole-monitor-common';
import { getEnvironment } from '@wormhole-foundation/wormhole-monitor-common';
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[] | null)[] = [];
try {
const vaaArray = Array.from(reobsMap.values());
// Process each VAA asynchronously and filter out any null results
reobs = (await Promise.all(vaaArray.map(processVaa))).filter((vaa) => vaa !== null); // Remove any VAA that failed conversion
} catch (e) {
console.error('error processing reobservations', e);
console.error('reobs', reobs);
res.sendStatus(500);
}
// Need to flatten the array of arrays before returning
const retVal = reobs.flat();
res.status(200).send(JSON.stringify(retVal));
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[] = [];
let realVaas: 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);
const MAX_SOLANA_VAAS_TO_REOBSERVE = 2;
// Can only process 2 Solana VAAs at a time due to rpc rate limits
// So we put the rest back in the collection
let solanaCount = 0;
for (const vaa of vaas) {
if (vaa.chain === CHAIN_ID_SOLANA) {
solanaCount++;
if (solanaCount > MAX_SOLANA_VAAS_TO_REOBSERVE) {
putBack.push(vaa);
continue;
}
}
realVaas.push(vaa);
}
console.log('number of real VAAs', realVaas.length);
}
t.update(collectionRef, { VAAs: putBack });
});
} catch (e) {
console.error('error getting reobserved VAAs', e);
return current;
}
for (const vaa of realVaas) {
if (!(await isVAASigned(getEnvironment(), vaa.vaaKey))) {
current.set(vaa.txhash, vaa);
}
}
console.log('number of reobservable VAAs that are not signed', current.size);
return current;
}
async function processVaa(vaa: ReobserveInfo): Promise<ReobserveInfo[] | null> {
let vaas: ReobserveInfo[] = [];
if (vaa.chain === CHAIN_ID_SOLANA) {
const origTxHash = vaa.txhash;
const convertedTxHash: string[] = await convertSolanaTxToAccts(origTxHash);
console.log(`Converted solana txHash ${origTxHash} to account ${convertedTxHash}`);
if (convertedTxHash.length === 0) {
console.error(`Failed to convert solana txHash ${origTxHash} to an account.`);
return null; // Indicate failure to convert
}
for (const account of convertedTxHash) {
vaas.push({ ...vaa, txhash: account }); // Return a new object with the updated txhash
}
} else {
vaas.push(vaa); // Return the original object for non-Solana chains
}
return vaas;
}