Explorar el Código

Merge pull request #105 from pyth-network/drozdziak1/p2w-relay-evm

Drozdziak1/p2w relay evm
Stanisław Drozd hace 3 años
padre
commit
9f36719d3b

+ 1 - 1
Tiltfile

@@ -298,7 +298,7 @@ if pyth:
     # Terra relay
     docker_build(
         ref = "p2w-terra-relay",
-        context = "third_party/pyth/p2w-terra-relay",
+        context = ".",
         dockerfile = "third_party/pyth/p2w-terra-relay/Dockerfile.pyth_relay",
     )
     k8s_yaml_with_ns("devnet/p2w-terra-relay.yaml")

+ 4 - 1
third_party/pyth/p2w-terra-relay/.gitignore

@@ -1 +1,4 @@
-/lib
+/lib
+
+# EVM artifacts
+/src/evm

+ 17 - 4
third_party/pyth/p2w-terra-relay/Dockerfile.pyth_relay

@@ -1,8 +1,21 @@
-FROM node:16-alpine
+FROM node:16-alpine@sha256:72a490e7ed8aed68e16b8dc8f37b5bcc35c5b5c56ee3256effcdee63e2546f93
 
-WORKDIR /app/pyth_relay
-COPY . .
-RUN npm install && npm run build && npm cache clean --force
+ARG P2W_BASE_PATH=/usr/src/pyth2wormhole
+
+RUN apk --no-cache --update add python3 build-base  # Needed by the Ethereum build
+
+WORKDIR ${P2W_BASE_PATH}/ethereum
+ADD ethereum .
+RUN npm install && npm run build
+
+RUN apk del build-base # No longer needed after EVM build
+
+ARG P2W_RELAY_REL_PATH=third_party/pyth/p2w-terra-relay
+
+WORKDIR ${P2W_BASE_PATH}/${P2W_RELAY_REL_PATH} 
+ADD ${P2W_RELAY_REL_PATH} .
+
+RUN npm ci && npm run build && npm cache clean --force
 
 # If you are building for production
 # RUN npm ci --only=production

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 275 - 91
third_party/pyth/p2w-terra-relay/package-lock.json


+ 7 - 1
third_party/pyth/p2w-terra-relay/package.json

@@ -4,7 +4,12 @@
   "description": "Pyth relayer",
   "main": "index.js",
   "scripts": {
-    "build": "tsc",
+    "build": "npm run build-evm && npm run build-lib",
+    "build-evm": "npm run build-evm-contract && npm run copy-evm-abis && npm run build-evm-bindings",
+    "build-lib": "tsc",
+    "build-evm-contract": "cd ../../../ethereum/ && (npm run build || npm ci && npm run build)",
+    "copy-evm-abis": "mkdir -p ./src/evm/abis && cp -r ../../../ethereum/build/contracts/* ./src/evm/abis/",
+    "build-evm-bindings": "mkdir -p ./src/evm/bindings/ && typechain --target=ethers-v5 --out-dir=src/evm/bindings/ src/evm/abis/Pyth*.json",
     "start": "node lib/index.js",
     "listen_only": "node lib/index.js --listen_only"
   },
@@ -31,6 +36,7 @@
     "@solana/spl-token": "^0.1.8",
     "@solana/web3.js": "^1.24.0",
     "@terra-money/terra.js": "^3.0.4",
+    "@typechain/ethers-v5": "^7.0.1",
     "@types/express": "^4.17.13",
     "async-mutex": "^0.3.2",
     "body-parser": "^1.19.0",

+ 22 - 10
third_party/pyth/p2w-terra-relay/src/listen.ts

@@ -147,25 +147,32 @@ async function processVaa(vaaBytes: string) {
   // );
 
   // logger.debug("listen:processVaa: parsedVAA: %o", parsedVAA);
-  
+
   let batchAttestation;
 
   try {
-    batchAttestation = helpers.parsePythBatchPriceAttestation(Buffer.from(parsedVAA.payload));
+    batchAttestation = helpers.parsePythBatchPriceAttestation(
+      Buffer.from(parsedVAA.payload)
+    );
   } catch (e: any) {
     logger.error(e, e.stack);
-    logger.error("Parsing failed. Dropping vaa: %o", parsedVAA)
+    logger.error("Parsing failed. Dropping vaa: %o", parsedVAA);
     return;
   }
 
-  let isAnyPriceNew = batchAttestation.priceAttestations.some(priceAttestation => {
-    const key = priceAttestation.priceId;
-    let lastSeqNum = seqMap.get(key);
-    return lastSeqNum === undefined || lastSeqNum < parsedVAA.sequence;
-  })
+  let isAnyPriceNew = batchAttestation.priceAttestations.some(
+    (priceAttestation) => {
+      const key = priceAttestation.priceId;
+      let lastSeqNum = seqMap.get(key);
+      return lastSeqNum === undefined || lastSeqNum < parsedVAA.sequence;
+    }
+  );
 
   if (!isAnyPriceNew) {
-    logger.debug("For all prices there exists an update with newer sequence number. batch price attestation: %o", batchAttestation);
+    logger.debug(
+      "For all prices there exists an update with newer sequence number. batch price attestation: %o",
+      batchAttestation
+    );
     return;
   }
 
@@ -192,6 +199,11 @@ async function processVaa(vaaBytes: string) {
   metrics.incIncoming();
   if (!listenOnly) {
     logger.debug("posting to worker");
-    await postEvent(vaaBytes, batchAttestation, parsedVAA.sequence, receiveTime);
+    await postEvent(
+      vaaBytes,
+      batchAttestation,
+      parsedVAA.sequence,
+      receiveTime
+    );
   }
 }

+ 16 - 4
third_party/pyth/p2w-terra-relay/src/promHelpers.ts

@@ -1,5 +1,6 @@
 import http = require("http");
 import client = require("prom-client");
+import helpers = require("./helpers");
 
 // NOTE:  To create a new metric:
 // 1) Create a private counter/gauge with appropriate name and help
@@ -109,7 +110,7 @@ export class PromHelper {
   addCompleteTime(val: number) {
     this.completeTime.observe(val);
   }
-  setWalletBalance(bal: number) {
+  setWalletBalance(bal: bigint) {
     this.walletReg.clear();
     // this.walletReg = new client.Registry();
     this.walletBalance = new client.Gauge({
@@ -120,9 +121,20 @@ export class PromHelper {
     });
     this.walletReg.registerMetric(this.walletBalance);
     let now = new Date();
-    // this.walletDate = now.toString();
-    this.walletBalance.set({ timestamp: now.toString() }, bal);
-    // this.walletBalance.set(bal);
+    let balance_converted: number | null = null;
+    // CAUTION(2022-03-22): Conversion to Number may overflow;
+    // at this time TS build fails without the conversion.
+    try {
+      balance_converted = Number(bal);
+    } catch (e) {
+      helpers.logger.error(
+        "setWalletBalance(): BigInt -> Number conversion failed"
+      );
+    }
+    // Do not crash if there's a problem with the balance value
+    if (balance_converted) {
+      this.walletBalance.set({ timestamp: now.toString() }, balance_converted);
+    }
   }
   incIncoming() {
     this.listenCounter.inc();

+ 44 - 0
third_party/pyth/p2w-terra-relay/src/relay/evm.ts

@@ -0,0 +1,44 @@
+import { Relay, RelayResult, RelayRetcode, PriceId } from "./iface";
+import { ethers } from "ethers";
+import { logger } from "../helpers";
+
+import {
+  PythImplementation__factory,
+  PythImplementation,
+} from "../evm/bindings/";
+
+export class EvmRelay implements Relay {
+  payerWallet: ethers.Wallet;
+  p2wContract: PythImplementation;
+  async relay(signedVAAs: Array<string>): Promise<RelayResult> {
+    logger.warn("EvmRelay.relay(): TODO(2021-03-22)");
+    return new RelayResult(RelayRetcode.Fail, []);
+  }
+  async query(priceId: PriceId): Promise<any> {
+    logger.warn("EvmRelay.relay(): TODO(2021-03-22)");
+    return new RelayResult(RelayRetcode.Fail, []);
+  }
+  async getPayerInfo(): Promise<{ address: string; balance: bigint }> {
+    return {
+      address: this.payerWallet.address,
+      balance: BigInt(`$(await this.payerWallet.getBalance())`),
+    };
+  }
+
+  constructor(cfg: {
+    rpcWsUrl: string;
+    payerWalletMnemonic: string;
+    payerWalletHDPath: string;
+    p2wContractAddress: string;
+  }) {
+    let provider = new ethers.providers.WebSocketProvider(cfg.rpcWsUrl);
+    let wallet = ethers.Wallet.fromMnemonic(
+      cfg.payerWalletMnemonic,
+      cfg.payerWalletHDPath
+    );
+
+    this.payerWallet = new ethers.Wallet(wallet.privateKey, provider);
+    let factory = new PythImplementation__factory(this.payerWallet);
+    this.p2wContract = factory.attach(cfg.p2wContractAddress);
+  }
+}

+ 4 - 2
third_party/pyth/p2w-terra-relay/src/relay/iface.ts

@@ -37,6 +37,8 @@ export interface Relay {
   /// Query price data on this chain
   query(priceId: PriceId): Promise<any>;
 
-  /// Monitor the payer account balance
-  getPayerInfo(): Promise<{ address: string; balance: number }>;
+  /// Monitor the payer account balance. Balance is a bigint (the JS
+  /// OG implementation) to accomodate for big number impl
+  /// differences.
+  getPayerInfo(): Promise<{ address: string; balance: bigint }>;
 }

+ 3 - 3
third_party/pyth/p2w-terra-relay/src/relay/terra.ts

@@ -8,7 +8,7 @@ import {
 import { hexToUint8Array } from "@certusone/wormhole-sdk";
 import { redeemOnTerra } from "@certusone/wormhole-sdk";
 
-import { logger, envOrErr } from "../helpers";
+import { logger } from "../helpers";
 
 import { Relay, RelayResult, RelayRetcode, PriceId } from "./iface";
 
@@ -178,7 +178,7 @@ export class TerraRelay implements Relay {
     });
   }
 
-  async getPayerInfo(): Promise<{ address: string; balance: number }> {
+  async getPayerInfo(): Promise<{ address: string; balance: bigint }> {
     const lcdClient = new LCDClient(this.lcdConfig);
 
     const mk = new MnemonicKey({
@@ -213,6 +213,6 @@ export class TerraRelay implements Relay {
       logger.error("failed to query coin balance: %o", e);
     }
 
-    return { address: wallet.key.accAddress, balance };
+    return { address: wallet.key.accAddress, balance: BigInt(balance) };
   }
 }

+ 4 - 9
third_party/pyth/p2w-terra-relay/src/rest.ts

@@ -30,15 +30,10 @@ export async function run() {
       res.json(result);
     });
 
-    app.get(
-      "/queryterra/:price_id",
-      async (req: Request, res: Response) => {
-        let result = await getPriceData(
-          req.params.price_id
-        );
-        res.json(result);
-      }
-    );
+    app.get("/queryterra/:price_id", async (req: Request, res: Response) => {
+      let result = await getPriceData(req.params.price_id);
+      res.json(result);
+    });
 
     app.get("/", (req: Request, res: Response) =>
       res.json(["/status", "/queryterra/<price_id>"])

+ 24 - 25
third_party/pyth/p2w-terra-relay/src/worker.ts

@@ -116,25 +116,26 @@ export async function run(met: PromHelper) {
       balanceQueryInterval = parseInt(process.env.BAL_QUERY_INTERVAL);
     }
 
-    let { address: payerAddress, balance: payerBalance } =
-      await relayImpl.getPayerInfo();
-    if (!isNaN(payerBalance)) {
-      walletTimeStamp = new Date();
-    }
-    if (balanceQueryInterval !== 0) {
-      logger.info(
-        "initial wallet balance is " +
-          payerBalance +
-          ", will query every " +
-          balanceQueryInterval +
-          " milliseconds."
-      );
-      metrics.setWalletBalance(payerBalance);
+    try {
+      let { address: payerAddress, balance: payerBalance } =
+        await relayImpl.getPayerInfo();
+      if (balanceQueryInterval !== 0) {
+        logger.info(
+          "initial wallet balance is " +
+            payerBalance +
+            ", will query every " +
+            balanceQueryInterval +
+            " milliseconds."
+        );
+        metrics.setWalletBalance(payerBalance);
 
-      nextBalanceQueryTimeAsMs = new Date().getTime() + balanceQueryInterval;
-    } else {
-      logger.info("initial wallet balance is " + payerBalance);
-      metrics.setWalletBalance(payerBalance);
+        nextBalanceQueryTimeAsMs = new Date().getTime() + balanceQueryInterval;
+      } else {
+        logger.info("initial wallet balance is " + payerBalance);
+        metrics.setWalletBalance(payerBalance);
+      }
+    } catch (e) {
+      walletTimeStamp = new Date();
     }
 
     await condition.wait(computeTimeout(), callBack);
@@ -396,13 +397,9 @@ async function finalizeEventsAlreadyLocked(
 async function updateBalance() {
   let now = new Date();
   if (balanceQueryInterval > 0 && now.getTime() >= nextBalanceQueryTimeAsMs) {
-    let { address, balance } = await relayImpl.getPayerInfo();
-    if (isNaN(balance)) {
-      logger.error("failed to query wallet balance!");
-    } else {
-      if (!isNaN(balance)) {
-        walletTimeStamp = new Date();
-      }
+    try {
+      let { address, balance } = await relayImpl.getPayerInfo();
+      walletTimeStamp = new Date();
       logger.info(
         "wallet " +
           address +
@@ -412,6 +409,8 @@ async function updateBalance() {
           walletTimeStamp.toISOString()
       );
       metrics.setWalletBalance(balance);
+    } catch (e) {
+      logger.error("failed to query wallet balance:" + e);
     }
     nextBalanceQueryTimeAsMs = now.getTime() + balanceQueryInterval;
   }

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio