1
1
module pyth_lazer ::pyth_lazer ;
2
2
3
- use pyth_lazer::channel;
4
- use pyth_lazer::feed::{Self , Feed };
5
3
use pyth_lazer::state::{Self , State };
6
- use pyth_lazer::i64::{Self };
7
- use pyth_lazer::i16::{Self };
8
4
use pyth_lazer::update::{Self , Update };
9
5
use sui::bcs;
10
6
use sui::clock::Clock ;
@@ -15,12 +11,10 @@ const UPDATE_MESSAGE_MAGIC: u32 = 1296547300;
15
11
const PAYLOAD_MAGIC : u32 = 2479346549 ;
16
12
17
13
// Error codes
18
- const EInvalidUpdate : u64 = 1 ;
19
14
const ESignerNotTrusted : u64 = 2 ;
20
15
const ESignerExpired : u64 = 3 ;
21
-
22
- // TODO:
23
- // error handling
16
+ const EInvalidMagic : u64 = 4 ;
17
+ const EInvalidPayloadLength : u64 = 6 ;
24
18
25
19
/// The `PYTH_LAZER` resource serves as the one-time witness.
26
20
/// It has the `drop` ability, allowing it to be consumed immediately after use.
@@ -83,161 +77,39 @@ public(package) fun verify_le_ecdsa_message(
83
77
/// * `update` - The LeEcdsa formatted Lazer update
84
78
///
85
79
/// # 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
87
82
/// * `ESignerNotTrusted` - The recovered public key is not in the trusted signers list
83
+ /// * `ESignerExpired` - The signer's certificate has expired
88
84
public fun parse_and_verify_le_ecdsa_update (s: &State , clock: &Clock , update: vector <u8 >): Update {
89
85
let mut cursor = bcs::new (update);
90
86
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
94
88
let magic = cursor.peel_u32 ();
95
- assert !(magic == UPDATE_MESSAGE_MAGIC , 0 );
89
+ assert !(magic == UPDATE_MESSAGE_MAGIC , EInvalidMagic );
96
90
91
+ // Parse signature
97
92
let mut signature = vector ::empty <u8 >();
98
-
99
93
let mut sig_i = 0 ;
100
94
while (sig_i < SECP256K1_SIG_LEN ) {
101
95
signature.push_back (cursor.peel_u8 ());
102
96
sig_i = sig_i + 1 ;
103
97
};
104
98
99
+ // Parse expected payload length and get remaining bytes as payload
105
100
let payload_len = cursor.peel_u16 ();
106
-
107
101
let payload = cursor.into_remainder_bytes ();
108
102
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 );
114
105
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 );
116
110
117
111
// Verify the signature against trusted signers
118
112
verify_le_ecdsa_message (s, clock, &signature, &payload);
119
113
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)
243
115
}
0 commit comments