| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
- import {PythImplementation__factory} from "./ethers-contracts";
- import * as http from "http";
- import * as net from "net";
- import fs from "fs";
- import {ethers} from "ethers";
- import {getSignedAttestation, parseBatchAttestation, p2w_core, sol_addr2buf} from "@certusone/p2w-sdk";
- import {setDefaultWasm, importCoreWasm} from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
- interface NewAttestationsResponse {
- pendingSeqnos: Array<number>,
- }
- async function readinessProbeRoutine(port: number) {
- let srv = net.createServer();
- return await srv.listen(port);
- }
- (async () => {
- // p2w-attest exposes an HTTP endpoint that shares the currently pending sequence numbers
- const P2W_ATTESTATIONS_HOST = process.env.P2W_ATTESTATIONS_HOST || "p2w-attest";
- const P2W_ATTESTATIONS_PORT = Number(process.env.P2W_ATTESTATIONS_PORT || "4343");
- const P2W_ATTESTATIONS_POLL_INTERVAL_MS = Number(process.env.P2W_ATTESTATIONS_POLL_INTERVAL_MS || "5000");
- const P2W_SOL_ADDRESS = process.env.P2W_SOL_ADDRESS || "P2WH424242424242424242424242424242424242424";
- const READINESS_PROBE_PORT = Number(process.env.READINESS_PROBE_PORT || "2000");
- const P2W_RELAY_RETRY_COUNT = Number(process.env.P2W_RELAY_RETRY_COUNT || "3");
- // ETH node connection details; Currently, we expect to read BIP44
- // wallet recovery mnemonics from a text file.
- const ETH_NODE_URL = process.env.ETH_NODE_URL || "ws://eth-devnet:8545";
- const ETH_P2W_CONTRACT = process.env.ETH_P2W_CONTRACT || "0xA94B7f0465E98609391C623d0560C5720a3f2D33";
- const ETH_MNEMONIC_FILE = process.env.ETH_MNEMONIC_FILE || "../../../ethereum/devnet_mnemonic.txt";
- const ETH_HD_WALLET_PATH = process.env.ETH_HD_WALLET_PATH || "m/44'/60'/0'/0/0";
- // Public RPC address for use with signed attestation queries
- const GUARDIAN_RPC_HOST_PORT = process.env.GUARDIAN_RPC_HOST_PORT || "http://guardian:7071";
- let readinessProbe = null;
- let seqnoPool: Map<number, number> = new Map();
- console.log(`Polling attestations endpoint every ${P2W_ATTESTATIONS_POLL_INTERVAL_MS / 1000} seconds`);
- setDefaultWasm("node");
- const {parse_vaa} = await importCoreWasm();
- let p2w_eth: any;
- // Connect to ETH
- try {
- let provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
- let mnemonic: string = fs.readFileSync(ETH_MNEMONIC_FILE).toString("utf-8").trim();
- let wallet = ethers.Wallet.fromMnemonic(mnemonic, ETH_HD_WALLET_PATH);
- console.log(`Using ETH wallet pubkey: ${wallet.publicKey}`);
- let signer = new ethers.Wallet(wallet.privateKey, provider);
- let balance = await signer.getBalance();
- console.log(`Account balance is ${balance}`);
- let factory = new PythImplementation__factory(signer);
- p2w_eth = factory.attach(ETH_P2W_CONTRACT);
- }
- catch(e) {
- console.error(`Error: Could not instantiate ETH contract:`, e);
- throw e;
- }
- while (true) {
- http.get({
- hostname: P2W_ATTESTATIONS_HOST,
- port: P2W_ATTESTATIONS_PORT,
- path: "/",
- agent: false
- }, (res) => {
- if (res.statusCode != 200) {
- console.error("Could not reach attestations endpoint", res);
- } else {
- let chunks: string[] = [];
- res.setEncoding("utf-8");
- res.on('data', (chunk) => {
- chunks.push(chunk);
- });
- res.on('end', () => {
- let body = chunks.join('');
- let response: NewAttestationsResponse = JSON.parse(body);
- console.log(`Got ${response.pendingSeqnos.length} new seqnos: ${response.pendingSeqnos}`);
- for (let seqno of response.pendingSeqnos) {
- seqnoPool.set(seqno, 0);
- }
- });
- }
- }).on('error', (e) => {
- console.error(`Got error: ${e.message}`);
- });
- console.log("Processing seqnos:", seqnoPool);
- for (let poolEntry of seqnoPool) {
- let seqno = poolEntry[0];
- let attempts = poolEntry[1];
- if (attempts >= P2W_RELAY_RETRY_COUNT) {
- console.warn(`[seqno ${poolEntry}] Exceeded retry count, removing from list`);
- seqnoPool.delete(seqno);
- continue;
- }
- let vaaResponse: any;
- try {
- vaaResponse = await getSignedAttestation(
- GUARDIAN_RPC_HOST_PORT,
- P2W_SOL_ADDRESS,
- seqno,
- {
- transport: NodeHttpTransport()
- }
- );
- }
- catch(e) {
- console.error(`[seqno ${poolEntry}] Error: Could not call getSignedAttestation:`, e);
- seqnoPool.set(seqno, attempts + 1);
- continue;
- }
- console.log(`[seqno ${poolEntry}] Price attestation VAA bytes:\n`, vaaResponse.vaaBytes);
- let parsedVaa = parse_vaa(vaaResponse.vaaBytes);
- console.log(`[seqno ${poolEntry}] Parsed VAA:\n`, parsedVaa);
- let parsedAttestations = await parseBatchAttestation(parsedVaa.payload);
- console.log(`[seqno ${poolEntry}] Parsed ${parsedAttestations.price_attestations.length} price attestations:\n`, parsedAttestations);
- // try {
- // let tx = await p2w_eth.attestPrice(vaaResponse.vaaBytes, {gasLimit: 1000000});
- // let retval = await tx.wait();
- // console.log(`[seqno ${poolEntry}] attestPrice() output:\n`, retval);
- // } catch(e) {
- // console.error(`[seqno ${poolEntry}, {parsedAttestations.length} symbols] Error: Could not call attestPrice() on ETH:`, e);
- // seqnoPool.set(seqno, attempts + 1);
- // continue;
- // }
- console.warn("TODO: implement relayer ETH call");
- // for (let att of parsedAttestations) {
- // let product_id = att.product_id;
- // let price_type = att.price_type == "Price" ? 1 : 0;
- // let latest_attestation: any;
- // try {
- // let p2w = await p2w_core();
- // console.log(`Looking up latestAttestation for `, product_id, price_type);
- // latest_attestation = await p2w_eth.latestAttestation(product_id, price_type);
- // } catch(e) {
- // console.error(`[seqno ${poolEntry}] Error: Could not call latestAttestation() on ETH:`, e);
- // seqnoPool.set(seqno, attempts + 1);
- // continue;
- // }
- // console.log(`[seqno ${poolEntry}] Latest price type ${price_type} attestation of ${product_id} is ${latest_attestation}`);
- // }
- if (!readinessProbe) {
- console.log(`[seqno ${poolEntry}] Attestation successful. Starting readiness probe.`);
- readinessProbe = readinessProbeRoutine(READINESS_PROBE_PORT);
- }
- seqnoPool.delete(seqno); // Everything went well, seqno no longer pending.
- }
- await new Promise(f => {setTimeout(f, P2W_ATTESTATIONS_POLL_INTERVAL_MS);});
- }
- })();
|