11module pyth_lazer ::pyth_lazer ;
22
3- use pyth_lazer::channel;
4- use pyth_lazer::feed::{Self , Feed };
53use pyth_lazer::state::{Self , State };
6- use pyth_lazer::i64::{Self };
7- use pyth_lazer::i16::{Self };
84use pyth_lazer::update::{Self , Update };
95use sui::bcs;
106use sui::clock::Clock ;
@@ -15,12 +11,10 @@ const UPDATE_MESSAGE_MAGIC: u32 = 1296547300;
1511const PAYLOAD_MAGIC : u32 = 2479346549 ;
1612
1713// Error codes
18- const EInvalidUpdate : u64 = 1 ;
1914const ESignerNotTrusted : u64 = 2 ;
2015const ESignerExpired : u64 = 3 ;
21-
22- // TODO:
23- // error handling
16+ const EInvalidMagic : u64 = 4 ;
17+ const EInvalidPayloadLength : u64 = 6 ;
2418
2519/// The `PYTH_LAZER` resource serves as the one-time witness.
2620/// It has the `drop` ability, allowing it to be consumed immediately after use.
@@ -83,161 +77,39 @@ public(package) fun verify_le_ecdsa_message(
8377/// * `update` - The LeEcdsa formatted Lazer update
8478///
8579/// # Errors
86- /// * `EInvalidUpdate` - Failed to parse the update according to the protocol definition
80+ /// * `EInvalidMagic` - Invalid magic number in update or payload
81+ /// * `EInvalidPayloadLength` - Payload length doesn't match actual data
8782/// * `ESignerNotTrusted` - The recovered public key is not in the trusted signers list
83+ /// * `ESignerExpired` - The signer's certificate has expired
8884public fun parse_and_verify_le_ecdsa_update (s: &State , clock: &Clock , update: vector <u8 >): Update {
8985 let mut cursor = bcs::new (update);
9086
91- // TODO: introduce helper functions to check data len before peeling. allows us to return more
92- // granular error messages.
93-
87+ // Parse and validate message magic
9488 let magic = cursor.peel_u32 ();
95- assert !(magic == UPDATE_MESSAGE_MAGIC , 0 );
89+ assert !(magic == UPDATE_MESSAGE_MAGIC , EInvalidMagic );
9690
91+ // Parse signature
9792 let mut signature = vector ::empty <u8 >();
98-
9993 let mut sig_i = 0 ;
10094 while (sig_i < SECP256K1_SIG_LEN ) {
10195 signature.push_back (cursor.peel_u8 ());
10296 sig_i = sig_i + 1 ;
10397 };
10498
99+ // Parse expected payload length and get remaining bytes as payload
105100 let payload_len = cursor.peel_u16 ();
106-
107101 let payload = cursor.into_remainder_bytes ();
108102
109- assert !((payload_len as u64 ) == payload.length (), 0 );
110-
111- let mut cursor = bcs::new (payload);
112- let payload_magic = cursor.peel_u32 ();
113- assert !(payload_magic == PAYLOAD_MAGIC , 0 );
103+ // Validate expectedpayload length
104+ assert !((payload_len as u64 ) == payload.length (), EInvalidPayloadLength );
114105
115- let timestamp = cursor.peel_u64 ();
106+ // Parse payload
107+ let mut payload_cursor = bcs::new (payload);
108+ let payload_magic = payload_cursor.peel_u32 ();
109+ assert !(payload_magic == PAYLOAD_MAGIC , EInvalidMagic );
116110
117111 // Verify the signature against trusted signers
118112 verify_le_ecdsa_message (s, clock, &signature, &payload);
119113
120- let channel_value = cursor.peel_u8 ();
121- let channel = if (channel_value == 0 ) {
122- channel::new_invalid ()
123- } else if (channel_value == 1 ) {
124- channel::new_real_time ()
125- } else if (channel_value == 2 ) {
126- channel::new_fixed_rate_50ms ()
127- } else if (channel_value == 3 ) {
128- channel::new_fixed_rate_200ms ()
129- } else {
130- channel::new_invalid () // Default to Invalid for unknown values
131- };
132-
133- let mut feeds = vector ::empty <Feed >();
134- let mut feed_i = 0 ;
135-
136- let feed_count = cursor.peel_u8 ();
137-
138- while (feed_i < feed_count) {
139- let feed_id = cursor.peel_u32 ();
140- let mut feed = feed::new (
141- feed_id,
142- option::none (),
143- option::none (),
144- option::none (),
145- option::none (),
146- option::none (),
147- option::none (),
148- option::none (),
149- option::none (),
150- option::none (),
151- );
152-
153- let properties_count = cursor.peel_u8 ();
154- let mut properties_i = 0 ;
155-
156- while (properties_i < properties_count) {
157- let property_id = cursor.peel_u8 ();
158-
159- if (property_id == 0 ) {
160- let price = cursor.peel_u64 ();
161- if (price != 0 ) {
162- feed.set_price (option::some (option::some (i64::from_u64 (price))));
163- } else {
164- feed.set_price (option::some (option::none ()));
165- }
166- } else if (property_id == 1 ) {
167- let best_bid_price = cursor.peel_u64 ();
168- if (best_bid_price != 0 ) {
169- feed.set_best_bid_price (
170- option::some (option::some (i64::from_u64 (best_bid_price))),
171- );
172- } else {
173- feed.set_best_bid_price (option::some (option::none ()));
174- }
175- } else if (property_id == 2 ) {
176- let best_ask_price = cursor.peel_u64 ();
177- if (best_ask_price != 0 ) {
178- feed.set_best_ask_price (
179- option::some (option::some (i64::from_u64 (best_ask_price))),
180- );
181- } else {
182- feed.set_best_ask_price (option::some (option::none ()));
183- }
184- } else if (property_id == 3 ) {
185- let publisher_count = cursor.peel_u16 ();
186- feed.set_publisher_count (option::some (publisher_count));
187- } else if (property_id == 4 ) {
188- let exponent = cursor.peel_u16 ();
189- feed.set_exponent (option::some (i16::from_u16 (exponent)));
190- } else if (property_id == 5 ) {
191- let confidence = cursor.peel_u64 ();
192- if (confidence != 0 ) {
193- feed.set_confidence (option::some (option::some (i64::from_u64 (confidence))));
194- } else {
195- feed.set_confidence (option::some (option::none ()));
196- }
197- } else if (property_id == 6 ) {
198- let exists = cursor.peel_u8 ();
199- if (exists == 1 ) {
200- let funding_rate = cursor.peel_u64 ();
201- feed.set_funding_rate (option::some (option::some (i64::from_u64 (funding_rate))));
202- } else {
203- feed.set_funding_rate (option::some (option::none ()));
204- }
205- } else if (property_id == 7 ) {
206- let exists = cursor.peel_u8 ();
207-
208- if (exists == 1 ) {
209- let funding_timestamp = cursor.peel_u64 ();
210- feed.set_funding_timestamp (option::some (option::some (funding_timestamp)));
211- } else {
212- feed.set_funding_timestamp (option::some (option::none ()));
213- }
214- } else if (property_id == 8 ) {
215- let exists = cursor.peel_u8 ();
216-
217- if (exists == 1 ) {
218- let funding_rate_interval = cursor.peel_u64 ();
219- feed.set_funding_rate_interval (
220- option::some (option::some (funding_rate_interval)),
221- );
222- } else {
223- feed.set_funding_rate_interval (option::some (option::none ()));
224- }
225- } else {
226- // When we have an unknown property, we do not know its length, and therefore
227- // we cannot ignore it and parse the next properties.
228- abort EInvalidUpdate // FIXME: return more granular error messages
229- };
230-
231- properties_i = properties_i + 1 ;
232- };
233-
234- vector ::push_back (&mut feeds, feed);
235-
236- feed_i = feed_i + 1 ;
237- };
238-
239- let remaining_bytes = cursor.into_remainder_bytes ();
240- assert !(remaining_bytes.length () == 0 , 0 );
241-
242- update::new (timestamp, channel, feeds)
114+ update::parse_from_cursor (payload_cursor)
243115}
0 commit comments