Browse Source

proof confirmation logic set up, callback function established

Ayush Suresh 4 months ago
parent
commit
d35a5eae22

+ 1 - 0
target_chains/stylus/Cargo.lock

@@ -4898,6 +4898,7 @@ version = "0.1.11"
 dependencies = [
  "alloy-primitives 0.8.20",
  "alloy-sol-types 0.8.20",
+ "byteorder",
  "dotenv",
  "ethers",
  "eyre",

+ 1 - 0
target_chains/stylus/contracts/pyth-receiver/Cargo.toml

@@ -12,6 +12,7 @@ description = "Stylus hello world example"
 alloy-primitives = "=0.8.20"
 alloy-sol-types = "=0.8.20"
 stylus-sdk = "0.9.0"
+byteorder = { version = "1.4.3" }
 hex = { version = "0.4", default-features = false }
 pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk" }
 pyth-types = { path = "../pyth-types" }

+ 102 - 15
target_chains/stylus/contracts/pyth-receiver/src/lib.rs

@@ -14,14 +14,21 @@ use stylus_sdk::{alloy_primitives::{U16, U32, U256, U64, I32, I64, FixedBytes, A
                 storage::{StorageGuardMut, StorageAddress, StorageVec, StorageMap, StorageUint, StorageBool, StorageU256, StorageU16, StorageFixedBytes},
                 call::Call};
 
-use structs::{PriceInfoReturn, PriceInfoStorage, DataSourceStorage, DataSource};
+use structs::{PriceInfoReturn, PriceInfoStorage, DataSourceStorage, DataSource, PriceInfo};
 use error::{PythReceiverError};
-use pythnet_sdk::{wire::{v1::{
+use pythnet_sdk::{wire::{from_slice, v1::{
             AccumulatorUpdateData, Proof,
+            PYTHNET_ACCUMULATOR_UPDATE_MAGIC,
+            WormholeMessage, WormholePayload,
         },
     },
+    accumulators::{merkle::{MerklePath, MerkleRoot}},
+    hashers::keccak256_160::Keccak160,
+    messages::Message
 };
-use wormhole_vaas::{Readable, Vaa, VaaBody, VaaHeader};
+use wormhole_vaas::{Readable, Vaa, VaaBody, VaaHeader, Writeable};
+
+const ACCUMULATOR_WORMHOLE_MAGIC: u32 = 0x41555756;
 
 sol_interface! {
     interface IWormholeContract {
@@ -121,16 +128,45 @@ impl PythReceiver {
     }
 
     pub fn update_price_feeds(&mut self, update_data: Vec<u8>) {
+        
+    }
+
+    pub fn update_price_feeds_if_necessary(
+        &mut self,
+        _update_data: Vec<Vec<u8>>,
+        _price_ids: Vec<[u8; 32]>,
+        _publish_times: Vec<u64>,
+    ) {
+        // dummy implementation
+    }
+
+    fn update_price_feeds_internal(&self, update_data: Vec<u8>, price_ids: Vec<Address>, min_publish_time: u64, max_publish_time: u64, unique: bool) -> Result<(), PythReceiverError> {
         let update_data_array: &[u8] = &update_data;
-        let update_data = AccumulatorUpdateData::try_from_slice(&update_data_array).unwrap();
+        // Check the first 4 bytes of the update_data_array for the magic header
+        if update_data_array.len() < 4 {
+            panic!("update_data too short for magic header check");
+        }
 
+        let mut header = [0u8; 4];
+        header.copy_from_slice(&update_data_array[0..4]);
+        
+        if &header != PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
+            panic!("Invalid update_data magic header");
+        }
+        
+        let update_data = AccumulatorUpdateData::try_from_slice(&update_data_array).unwrap();
+        
         match update_data.proof {
             Proof::WormholeMerkle { vaa, updates } => {
                 let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
-                let config = Call::new_in(self);
+                let config = Call::new();
                 let parsed_vaa = wormhole.parse_and_verify_vm(config, Vec::from(vaa)).map_err(|_| PythReceiverError::PriceUnavailable).unwrap();
                 let vaa = Vaa::read(&mut parsed_vaa.as_slice()).unwrap();
 
+                // TODO: CHECK IF THE VAA IS FROM A VALID DATA SOURCE
+
+                let root_digest: MerkleRoot<Keccak160> = parse_wormhole_proof(vaa).unwrap();
+
                 for update in updates {
                     // fill in update processing logic.
                     // update is a merkle price update
@@ -140,22 +176,44 @@ impl PythReceiver {
                     //     pub proof: MerklePath<Keccak160>,
                     // }
 
-                    let message = update.message;
-                    let proof = update.proof;
+                    let message_vec = Vec::from(update.message);
+                    let proof: MerklePath<Keccak160> = update.proof;
+
+                    if !root_digest.check(proof, &message_vec) {
+                        return Err(PythReceiverError::PriceUnavailable);
+                    }
 
+                    // TODO: UPDATE THE PRICE INFO
+                    let msg = from_slice::<byteorder::BE, Message>(&message_vec)
+                        .map_err(|_| PythReceiverError::PriceUnavailable)?;
                     
+                    match msg {
+                        Message::PriceFeedMessage(price_feed_message) => {
+                            // if self.update_price_feed_if_new(price_feed_message.feed_id,PriceInfoStorage::from(&price_feed_message)) {
+                            //     count_updates += 1;
+                            // }
+
+                        },
+                        Message::TwapMessage(_) => {
+                            // Handle TWAP message - currently not implemented
+                            // This could be extended to handle TWAP price updates
+                        },
+                        Message::PublisherStakeCapsMessage(_) => {
+                            // Handle publisher stake caps message - currently not implemented
+                            // This could be extended to handle publisher stake updates
+                        },
+                    }
+
+
+                    // TODO: STORE PRICE INFO IN OUTPUT 
+
                 }
+
+                // TODO: FORM OUTPUT ARRAY
             }
         };
-    }
 
-    pub fn update_price_feeds_if_necessary(
-        &mut self,
-        _update_data: Vec<Vec<u8>>,
-        _price_ids: Vec<[u8; 32]>,
-        _publish_times: Vec<u64>,
-    ) {
-        // dummy implementation
+        Ok(())
     }
 
     pub fn get_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {
@@ -213,4 +271,33 @@ impl PythReceiver {
         
         current_u64.saturating_sub(publish_time_u64) <= max_age
     }
+
+    // Updates the Price Feed only if it is newer than the current one. This function never fails
+    // and will either update in-place or not update at all. The return value indicates whether
+    // the update was performed or not.
+    // fn update_price_feed_if_new(&mut self, price_id: [u8; 32], price_feed: PriceInfo) -> bool {
+    //     match self.latest_price_info.get(&price_id) {
+    //         Some(stored_price_feed) => {
+    //             let update = price_feed.price.publish_time > stored_price_feed.price.publish_time;
+    //             update.then(|| self.prices.insert(&price_feed.id, &price_feed));
+    //             update
+    //         }
+
+    //         None => {
+    //             self.prices.insert(&price_feed.id, &price_feed);
+    //             true
+    //         }
+    //     }
+    // }
+
+    
 }
+
+fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
+    let message = WormholeMessage::try_from_bytes(vaa.body.payload.to_vec())
+                .map_err(|_| PythReceiverError::PriceUnavailable)?;
+    let root: MerkleRoot<Keccak160> = MerkleRoot::new(match message.payload {
+        WormholePayload::Merkle(merkle_root) => merkle_root.root,
+    });
+    Ok(root)
+}

+ 24 - 1
target_chains/stylus/contracts/pyth-receiver/src/structs.rs

@@ -3,9 +3,10 @@ use stylus_sdk::{
     prelude::*,
     storage::{
         StorageU64, StorageI32, StorageI64, StorageU16, StorageFixedBytes, StorageKey
-    }
+    },
 };
 use stylus_sdk::alloy_primitives::{U16, FixedBytes,U64, I32, I64, B256, U256, keccak256};
+use pythnet_sdk::messages::PriceFeedMessage;
 
 #[derive(Debug)]
 #[storage]
@@ -77,5 +78,27 @@ pub struct PriceInfoStorage {
     pub ema_conf: StorageU64,
 }
 
+pub struct PriceInfo {
+    pub publish_time: U64,
+    pub expo: I32,
+    pub price: I64,
+    pub conf: U64,
+    pub ema_price: I64,
+    pub ema_conf: U64,
+}
+
+impl From<&PriceFeedMessage> for PriceInfo {
+    fn from(price_feed_message: &PriceFeedMessage) -> Self {
+        Self {
+            publish_time: U64::from(price_feed_message.publish_time),
+            expo: I32::from_be_bytes(price_feed_message.exponent.to_be_bytes()),
+            price: I64::from_be_bytes(price_feed_message.price.to_be_bytes()),
+            conf: U64::from(price_feed_message.conf),
+            ema_price: I64::from_be_bytes(price_feed_message.ema_price.to_be_bytes()),
+            ema_conf: U64::from(price_feed_message.ema_conf),
+        }
+    }
+}
+
 // PriceInfo struct storing price information
 pub type PriceInfoReturn = (U64, I32, I64, U64, I64, U64);