Procházet zdrojové kódy

p2w-sdk: Use wasm in the JS package, detect bundler/node via `window`

This commit makes sure that our wasm usage is more robust. We ensure
that the JSON representation renders all important numbers in their
string decimal form. b

commit-id:75f9c224
Stan Drozd před 3 roky
rodič
revize
0dcee4db5f

+ 1 - 1
third_party/pyth/p2w-integration-observer/src/index.ts

@@ -143,7 +143,7 @@ async function readinessProbeRoutine(port: number) {
 
 	    let parsedAttestations = (await parseBatchPriceAttestation(Buffer.from(parsedVaa.payload))).priceAttestations;
 
-	    console.log(`[seqno ${poolEntry}] Parsed ${parsedAttestations.length} price attestations:\n`, parsedAttestations);
+	    console.log(`[seqno ${poolEntry}] Parsed ${parsedAttestations.length} prices in batch:\n`, parsedAttestations);
 
 	    // try {
 	    // 	let tx = await p2w_eth.attestPrice(vaaResponse.vaaBytes, {gasLimit: 1000000});

+ 1 - 95
third_party/pyth/p2w-sdk/js/src/index.ts

@@ -16,92 +16,6 @@ async function importWasm() {
     return _P2W_WASM;
 }
 
-
-/*
-  // Definitions exist in p2w-sdk/rust/
-  
-  struct Rational {
-      int64 value;
-      int64 numerator;
-      int64 denominator;
-  }
-
-  struct PriceAttestation {
-      uint32 magic; // constant "P2WH"
-      uint16 version;
-
-      // PayloadID uint8 = 1
-      uint8 payloadId;
-
-      bytes32 productId;
-      bytes32 priceId;
-
-      uint8 priceType;
-
-      int64 price;
-      int32 exponent;
-
-      Rational emaPrice;
-      Rational emaConfidence;
-
-      uint64 confidenceInterval;
-
-      uint8 status;
-      uint8 corpAct;
-
-      uint64 timestamp;
-  }
-
-0   uint32        magic // constant "P2WH"
-4   u16           version
-6   u8            payloadId // 1
-7   [u8; 32]      productId
-39  [u8; 32]      priceId
-71  u8            priceType
-72  i64           price
-80  i32           exponent
-84  PythRational  emaPrice
-108 PythRational  emaConfidence
-132 u64           confidenceInterval
-140 u8            status
-141 u8            corpAct
-142 u64           timestamp
-
-In version 2 prices are sent in batch with the following structure:
-
-  struct BatchPriceAttestation {
-      uint32 magic; // constant "P2WH"
-      uint16 version;
-
-      // PayloadID uint8 = 2
-      uint8 payloadId;
-
-      // number of attestations 
-      uint16 nAttestations;
-
-      // Length of each price attestation in bytes
-      //
-      // This field is provided for forwards compatibility. Fields in future may be added in
-      // an append-only way allowing for parsers to continue to work by parsing only up to
-      // the fields they know, leaving unread input in the buffer. Code may still need to work
-      // with the full size of the value however, such as when iterating over lists of attestations,
-      // for these use-cases the structure size is included as a field.
-      //
-      // attestation_size >= 150
-      uint16 attestationSize;
-      
-      priceAttestations: PriceAttestation[]
-  }
-
-0   uint32    magic // constant "P2WH"
-4   u16       version
-6   u8        payloadId // 2
-7   u16       n_attestations
-9   u16       attestation_size // >= 150
-11  ..        price_attestation (Size: attestation_size x [n_attestations])
-
-*/
-
 export type Rational = {
     value: BigInt;
     numerator: BigInt;
@@ -157,15 +71,7 @@ export async function parseBatchPriceAttestation(
     let wasm = await importWasm();
     let rawVal = await wasm.parse_batch_attestation(arr);
 
-    let priceAttestations = [];
-
-    for (let rawAttestation of rawVal.price_attestations) {
-	priceAttestations.push(rawToPriceAttestation(rawAttestation));
-    }
-
-    return {
-        priceAttestations,
-    };
+    return rawVal;
 }
 
 // Returns a hash of all priceIds within the batch, it can be used to identify whether there is a

+ 44 - 3
third_party/pyth/p2w-sdk/rust/src/lib.rs

@@ -6,6 +6,11 @@
 //! similar human-readable names and provide a failsafe for some of
 //! the probable adversarial scenarios.
 
+use serde::{
+    Serialize,
+    Serializer,
+};
+
 use std::borrow::Borrow;
 use std::convert::TryInto;
 use std::io::Read;
@@ -27,6 +32,14 @@ use solana_program::pubkey::Pubkey;
 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
 pub mod wasm;
 
+#[cfg(feature = "wasm")]
+#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
+use wasm_bindgen::prelude::*;
+
+pub mod utils;
+
+use utils::P2WPriceStatus;
+
 pub type ErrBox = Box<dyn std::error::Error>;
 
 /// Precedes every message implementing the p2w serialization format
@@ -63,26 +76,53 @@ pub enum PayloadId {
 /// Important: For maximum security, *both* product_id and price_id
 /// should be used as storage keys for known attestations in target
 /// chain logic.
+///
+/// NOTE(2022-04-25): the serde attributes help prevent math errors,
+/// and no less annoying low-effort serialization override method is known.
 #[derive(Clone, Default, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
 pub struct PriceAttestation {
     pub product_id:         Pubkey,
     pub price_id:           Pubkey,
+    #[serde(serialize_with = "use_to_string")]
     pub price:              i64,
+    #[serde(serialize_with = "use_to_string")]
     pub conf:               u64,
+    #[serde(serialize_with = "use_to_string")]
     pub expo:               i32,
+    #[serde(serialize_with = "use_to_string")]
     pub ema_price:          i64,
+    #[serde(serialize_with = "use_to_string")]
     pub ema_conf:           u64,
+    #[serde(with = "P2WPriceStatus")]
     pub status:             PriceStatus,
+    #[serde(serialize_with = "use_to_string")]
     pub num_publishers:     u32,
+    #[serde(serialize_with = "use_to_string")]
     pub max_num_publishers: u32,
+    #[serde(serialize_with = "use_to_string")]
     pub attestation_time:   UnixTimestamp,
+    #[serde(serialize_with = "use_to_string")]
     pub publish_time:       UnixTimestamp,
+    #[serde(serialize_with = "use_to_string")]
     pub prev_publish_time:  UnixTimestamp,
+    #[serde(serialize_with = "use_to_string")]
     pub prev_price:         i64,
+    #[serde(serialize_with = "use_to_string")]
     pub prev_conf:          u64,
 }
 
+/// Helper allowing ToString implementers to be serialized as strings accordingly
+pub fn use_to_string<T, S>(val: &T, s: S) -> Result<S::Ok, S::Error>
+where
+    T: ToString,
+    S: Serializer,
+{
+    s.serialize_str(&val.to_string())
+}
+
 #[derive(Clone, Default, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
 pub struct BatchPriceAttestation {
     pub price_attestations: Vec<PriceAttestation>,
 }
@@ -255,7 +295,7 @@ impl PriceAttestation {
             expo: price.expo,
             ema_price: price.ema_price.val,
             ema_conf: price.ema_conf.val as u64,
-            status: price.agg.status,
+            status: price.agg.status.into(),
             num_publishers: price.num_qt,
             max_num_publishers: price.num,
             attestation_time,
@@ -416,7 +456,7 @@ impl PriceAttestation {
             expo,
             ema_price,
             ema_conf,
-            status,
+            status: status.into(),
             num_publishers,
             max_num_publishers,
             attestation_time,
@@ -447,7 +487,7 @@ mod tests {
             ema_price:          -42,
             ema_conf:           42,
             expo:               -3,
-            status:             PriceStatus::Trading,
+            status:             PriceStatus::Trading.into(),
             num_publishers:     123212u32,
             max_num_publishers: 321232u32,
             attestation_time:   (0xdeadbeeffadedeedu64) as i64,
@@ -501,6 +541,7 @@ mod tests {
         let batch_attestation = BatchPriceAttestation {
             price_attestations: attestations,
         };
+        println!("Batch hex struct: {:#02X?}", batch_attestation);
 
         let serialized = batch_attestation.serialize()?;
         println!("Batch hex Bytes: {:02X?}", serialized);

+ 40 - 0
third_party/pyth/p2w-sdk/rust/src/utils.rs

@@ -0,0 +1,40 @@
+//! Utility types and functions
+use pyth_sdk_solana::state::PriceStatus;
+
+/// Helps add wasm serialization functionality to upstream PriceStatus
+#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq,)]
+#[repr(u8)]
+#[serde(remote = "PriceStatus")]
+pub enum P2WPriceStatus {
+    Unknown,
+    Trading,
+    Halted,
+    Auction
+}
+impl From<PriceStatus> for P2WPriceStatus {
+    fn from(ps: PriceStatus) -> Self {
+        match ps {
+            PriceStatus::Unknown => Self::Unknown,
+            PriceStatus::Trading => Self::Trading,
+            PriceStatus::Halted => Self::Halted,
+            PriceStatus::Auction => Self::Auction,
+        }
+    }
+}
+
+impl Into<PriceStatus> for P2WPriceStatus {
+    fn into(self) -> PriceStatus {
+        match self {
+            Self::Unknown => PriceStatus::Unknown,
+            Self::Trading => PriceStatus::Trading,
+            Self::Halted => PriceStatus::Halted,
+            Self::Auction => PriceStatus::Auction,
+        }
+    }
+}
+
+impl Default for P2WPriceStatus {
+    fn default() -> Self {
+        PriceStatus::default().into()
+    }
+}