Skip to content

Commit d35a5ea

Browse files
committed
proof confirmation logic set up, callback function established
1 parent 1ceadcf commit d35a5ea

File tree

4 files changed

+128
-16
lines changed

4 files changed

+128
-16
lines changed

target_chains/stylus/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/stylus/contracts/pyth-receiver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ description = "Stylus hello world example"
1212
alloy-primitives = "=0.8.20"
1313
alloy-sol-types = "=0.8.20"
1414
stylus-sdk = "0.9.0"
15+
byteorder = { version = "1.4.3" }
1516
hex = { version = "0.4", default-features = false }
1617
pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk" }
1718
pyth-types = { path = "../pyth-types" }

target_chains/stylus/contracts/pyth-receiver/src/lib.rs

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ use stylus_sdk::{alloy_primitives::{U16, U32, U256, U64, I32, I64, FixedBytes, A
1414
storage::{StorageGuardMut, StorageAddress, StorageVec, StorageMap, StorageUint, StorageBool, StorageU256, StorageU16, StorageFixedBytes},
1515
call::Call};
1616

17-
use structs::{PriceInfoReturn, PriceInfoStorage, DataSourceStorage, DataSource};
17+
use structs::{PriceInfoReturn, PriceInfoStorage, DataSourceStorage, DataSource, PriceInfo};
1818
use error::{PythReceiverError};
19-
use pythnet_sdk::{wire::{v1::{
19+
use pythnet_sdk::{wire::{from_slice, v1::{
2020
AccumulatorUpdateData, Proof,
21+
PYTHNET_ACCUMULATOR_UPDATE_MAGIC,
22+
WormholeMessage, WormholePayload,
2123
},
2224
},
25+
accumulators::{merkle::{MerklePath, MerkleRoot}},
26+
hashers::keccak256_160::Keccak160,
27+
messages::Message
2328
};
24-
use wormhole_vaas::{Readable, Vaa, VaaBody, VaaHeader};
29+
use wormhole_vaas::{Readable, Vaa, VaaBody, VaaHeader, Writeable};
30+
31+
const ACCUMULATOR_WORMHOLE_MAGIC: u32 = 0x41555756;
2532

2633
sol_interface! {
2734
interface IWormholeContract {
@@ -121,16 +128,45 @@ impl PythReceiver {
121128
}
122129

123130
pub fn update_price_feeds(&mut self, update_data: Vec<u8>) {
131+
132+
}
133+
134+
pub fn update_price_feeds_if_necessary(
135+
&mut self,
136+
_update_data: Vec<Vec<u8>>,
137+
_price_ids: Vec<[u8; 32]>,
138+
_publish_times: Vec<u64>,
139+
) {
140+
// dummy implementation
141+
}
142+
143+
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> {
124144
let update_data_array: &[u8] = &update_data;
125-
let update_data = AccumulatorUpdateData::try_from_slice(&update_data_array).unwrap();
145+
// Check the first 4 bytes of the update_data_array for the magic header
146+
if update_data_array.len() < 4 {
147+
panic!("update_data too short for magic header check");
148+
}
126149

150+
let mut header = [0u8; 4];
151+
header.copy_from_slice(&update_data_array[0..4]);
152+
153+
if &header != PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
154+
panic!("Invalid update_data magic header");
155+
}
156+
157+
let update_data = AccumulatorUpdateData::try_from_slice(&update_data_array).unwrap();
158+
127159
match update_data.proof {
128160
Proof::WormholeMerkle { vaa, updates } => {
129161
let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
130-
let config = Call::new_in(self);
162+
let config = Call::new();
131163
let parsed_vaa = wormhole.parse_and_verify_vm(config, Vec::from(vaa)).map_err(|_| PythReceiverError::PriceUnavailable).unwrap();
132164
let vaa = Vaa::read(&mut parsed_vaa.as_slice()).unwrap();
133165

166+
// TODO: CHECK IF THE VAA IS FROM A VALID DATA SOURCE
167+
168+
let root_digest: MerkleRoot<Keccak160> = parse_wormhole_proof(vaa).unwrap();
169+
134170
for update in updates {
135171
// fill in update processing logic.
136172
// update is a merkle price update
@@ -140,22 +176,44 @@ impl PythReceiver {
140176
// pub proof: MerklePath<Keccak160>,
141177
// }
142178

143-
let message = update.message;
144-
let proof = update.proof;
179+
let message_vec = Vec::from(update.message);
180+
let proof: MerklePath<Keccak160> = update.proof;
181+
182+
if !root_digest.check(proof, &message_vec) {
183+
return Err(PythReceiverError::PriceUnavailable);
184+
}
145185

186+
// TODO: UPDATE THE PRICE INFO
187+
let msg = from_slice::<byteorder::BE, Message>(&message_vec)
188+
.map_err(|_| PythReceiverError::PriceUnavailable)?;
146189

190+
match msg {
191+
Message::PriceFeedMessage(price_feed_message) => {
192+
// if self.update_price_feed_if_new(price_feed_message.feed_id,PriceInfoStorage::from(&price_feed_message)) {
193+
// count_updates += 1;
194+
// }
195+
196+
},
197+
Message::TwapMessage(_) => {
198+
// Handle TWAP message - currently not implemented
199+
// This could be extended to handle TWAP price updates
200+
},
201+
Message::PublisherStakeCapsMessage(_) => {
202+
// Handle publisher stake caps message - currently not implemented
203+
// This could be extended to handle publisher stake updates
204+
},
205+
}
206+
207+
208+
// TODO: STORE PRICE INFO IN OUTPUT
209+
147210
}
211+
212+
// TODO: FORM OUTPUT ARRAY
148213
}
149214
};
150-
}
151215

152-
pub fn update_price_feeds_if_necessary(
153-
&mut self,
154-
_update_data: Vec<Vec<u8>>,
155-
_price_ids: Vec<[u8; 32]>,
156-
_publish_times: Vec<u64>,
157-
) {
158-
// dummy implementation
216+
Ok(())
159217
}
160218

161219
pub fn get_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {
@@ -213,4 +271,33 @@ impl PythReceiver {
213271

214272
current_u64.saturating_sub(publish_time_u64) <= max_age
215273
}
274+
275+
// Updates the Price Feed only if it is newer than the current one. This function never fails
276+
// and will either update in-place or not update at all. The return value indicates whether
277+
// the update was performed or not.
278+
// fn update_price_feed_if_new(&mut self, price_id: [u8; 32], price_feed: PriceInfo) -> bool {
279+
// match self.latest_price_info.get(&price_id) {
280+
// Some(stored_price_feed) => {
281+
// let update = price_feed.price.publish_time > stored_price_feed.price.publish_time;
282+
// update.then(|| self.prices.insert(&price_feed.id, &price_feed));
283+
// update
284+
// }
285+
286+
// None => {
287+
// self.prices.insert(&price_feed.id, &price_feed);
288+
// true
289+
// }
290+
// }
291+
// }
292+
293+
216294
}
295+
296+
fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
297+
let message = WormholeMessage::try_from_bytes(vaa.body.payload.to_vec())
298+
.map_err(|_| PythReceiverError::PriceUnavailable)?;
299+
let root: MerkleRoot<Keccak160> = MerkleRoot::new(match message.payload {
300+
WormholePayload::Merkle(merkle_root) => merkle_root.root,
301+
});
302+
Ok(root)
303+
}

target_chains/stylus/contracts/pyth-receiver/src/structs.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use stylus_sdk::{
33
prelude::*,
44
storage::{
55
StorageU64, StorageI32, StorageI64, StorageU16, StorageFixedBytes, StorageKey
6-
}
6+
},
77
};
88
use stylus_sdk::alloy_primitives::{U16, FixedBytes,U64, I32, I64, B256, U256, keccak256};
9+
use pythnet_sdk::messages::PriceFeedMessage;
910

1011
#[derive(Debug)]
1112
#[storage]
@@ -77,5 +78,27 @@ pub struct PriceInfoStorage {
7778
pub ema_conf: StorageU64,
7879
}
7980

81+
pub struct PriceInfo {
82+
pub publish_time: U64,
83+
pub expo: I32,
84+
pub price: I64,
85+
pub conf: U64,
86+
pub ema_price: I64,
87+
pub ema_conf: U64,
88+
}
89+
90+
impl From<&PriceFeedMessage> for PriceInfo {
91+
fn from(price_feed_message: &PriceFeedMessage) -> Self {
92+
Self {
93+
publish_time: U64::from(price_feed_message.publish_time),
94+
expo: I32::from_be_bytes(price_feed_message.exponent.to_be_bytes()),
95+
price: I64::from_be_bytes(price_feed_message.price.to_be_bytes()),
96+
conf: U64::from(price_feed_message.conf),
97+
ema_price: I64::from_be_bytes(price_feed_message.ema_price.to_be_bytes()),
98+
ema_conf: U64::from(price_feed_message.ema_conf),
99+
}
100+
}
101+
}
102+
80103
// PriceInfo struct storing price information
81104
pub type PriceInfoReturn = (U64, I32, I64, U64, I64, U64);

0 commit comments

Comments
 (0)