Skip to content

Commit 2d9c6d3

Browse files
committed
feat(target_chains/starknet): wormhole VAA verification and parsing
1 parent 508de75 commit 2d9c6d3

File tree

4 files changed

+639
-11
lines changed

4 files changed

+639
-11
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scarb 2.5.4
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
mod pyth;
22
mod wormhole;
3+
mod reader;
Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
use core::option::OptionTrait;
2+
use core::array::ArrayTrait;
3+
use core::keccak::cairo_keccak;
4+
use core::integer::u128_byte_reverse;
5+
use core::fmt::{Debug, Formatter};
6+
7+
pub const EOF: felt252 = 'unexpected end of input';
8+
pub const UNEXPECTED_OVERFLOW: felt252 = 'unexpected overflow';
9+
10+
/// A byte array with storage format similar to `core::ByteArray`, but
11+
/// suitable for reading data from it.
12+
#[derive(Drop, Clone, Serde)]
13+
pub struct ByteArray {
14+
// Number of bytes stored in the last item of `self.data` (or 0 if it's empty).
15+
num_last_bytes: u8,
16+
// Bytes in big endian. Each item except the last one stores 31 bytes.
17+
// If `num_last_bytes < 31`, unused most significant bytes of the last item will be unused.
18+
data: Array<bytes31>,
19+
}
20+
21+
impl DebugByteArray of Debug<ByteArray> {
22+
fn fmt(self: @ByteArray, ref f: Formatter) -> Result<(), core::fmt::Error> {
23+
write!(f, "ByteArray {{ num_last_bytes: {}, data: [", self.num_last_bytes)?;
24+
let mut data = self.data.clone();
25+
loop {
26+
match data.pop_front() {
27+
Option::Some(v) => {
28+
let v: u256 = v.into();
29+
write!(f, "{:?}, ", v).unwrap();
30+
},
31+
Option::None => { break; },
32+
}
33+
};
34+
write!(f, "]}}")
35+
}
36+
}
37+
38+
#[generate_trait]
39+
pub impl ByteArrayImpl of ByteArrayTrait {
40+
/// Creates a byte array with the data.
41+
fn new(data: Array<bytes31>, num_last_bytes: u8) -> ByteArray {
42+
if data.len() == 0 {
43+
assert!(num_last_bytes == 0);
44+
} else {
45+
assert!(num_last_bytes <= 31);
46+
// TODO: check that unused bytes are zeroed.
47+
}
48+
ByteArray { num_last_bytes, data }
49+
}
50+
51+
/// Removes 31 or less bytes from the start of the array.
52+
/// Returns the value and the number of bytes.
53+
fn pop_front(ref self: ByteArray) -> Option<(bytes31, u8)> {
54+
let item = self.data.pop_front()?;
55+
if self.data.is_empty() {
56+
let num_bytes = self.num_last_bytes;
57+
self.num_last_bytes = 0;
58+
Option::Some((item, num_bytes))
59+
} else {
60+
Option::Some((item, 31))
61+
}
62+
}
63+
64+
fn len(self: @ByteArray) -> usize {
65+
if self.data.is_empty() {
66+
0
67+
} else {
68+
(self.data.len() - 1) * 31 + (*self.num_last_bytes).into()
69+
}
70+
}
71+
}
72+
73+
/// Allows to read data from a byte array.
74+
/// Uses big endian unless specified otherwise.
75+
/// All methods return `EOF` error if attempted to
76+
/// read more bytes than is available.
77+
#[derive(Drop, Clone)]
78+
pub struct Reader {
79+
// Input array.
80+
array: ByteArray,
81+
// Current value to read from (in big endian).
82+
current: u128,
83+
// Number of remaining bytes in `self.current`.
84+
num_current_bytes: u8,
85+
// Next value to read from (in big endian). This is needed because
86+
// `array.pop_front()` returns up to 31 bytes which require two u128 to store.
87+
next: Option<u128>,
88+
}
89+
90+
#[generate_trait]
91+
pub impl ReaderImpl of ReaderTrait {
92+
fn new(array: ByteArray) -> Reader {
93+
Reader { array, current: 0, num_current_bytes: 0, next: Option::None }
94+
}
95+
96+
/// Reads the specified number of bytes (up to 16) as a big endian unsigned integer.
97+
fn read(ref self: Reader, num_bytes: u8) -> Result<u128, felt252> {
98+
if num_bytes <= self.num_current_bytes {
99+
let x = self.read_from_current(num_bytes);
100+
return Result::Ok(x);
101+
}
102+
let num_low_bytes = num_bytes - self.num_current_bytes;
103+
let high = self.current;
104+
self.fetch_next()?;
105+
let low = self.read(num_low_bytes)?;
106+
let value = if num_low_bytes == 16 {
107+
low
108+
} else {
109+
high * one_shift_left_bytes_u128(num_low_bytes) + low
110+
};
111+
Result::Ok(value)
112+
}
113+
114+
fn read_u256(ref self: Reader) -> Result<u256, felt252> {
115+
let high = self.read(16)?;
116+
let low = self.read(16)?;
117+
let value = u256 { high, low };
118+
Result::Ok(value)
119+
}
120+
fn read_u128(ref self: Reader) -> Result<u128, felt252> {
121+
self.read(16)
122+
}
123+
fn read_u64(ref self: Reader) -> Result<u64, felt252> {
124+
let value = self.read(8)?.try_into().expect(UNEXPECTED_OVERFLOW);
125+
Result::Ok(value)
126+
}
127+
fn read_u32(ref self: Reader) -> Result<u32, felt252> {
128+
let value = self.read(4)?.try_into().expect(UNEXPECTED_OVERFLOW);
129+
Result::Ok(value)
130+
}
131+
fn read_u16(ref self: Reader) -> Result<u16, felt252> {
132+
let value = self.read(2)?.try_into().expect(UNEXPECTED_OVERFLOW);
133+
Result::Ok(value)
134+
}
135+
fn read_u8(ref self: Reader) -> Result<u8, felt252> {
136+
let value = self.read(1)?.try_into().expect(UNEXPECTED_OVERFLOW);
137+
Result::Ok(value)
138+
}
139+
140+
// TODO: skip without calculating values
141+
fn skip(ref self: Reader, mut num_bytes: u8) -> Result<(), felt252> {
142+
let mut result = Result::Ok(());
143+
while num_bytes > 0 {
144+
if num_bytes > 16 {
145+
match self.read(16) {
146+
Result::Ok(_) => {},
147+
Result::Err(err) => {
148+
result = Result::Err(err);
149+
break;
150+
}
151+
}
152+
num_bytes -= 16;
153+
} else {
154+
match self.read(num_bytes) {
155+
Result::Ok(_) => {},
156+
Result::Err(err) => {
157+
result = Result::Err(err);
158+
break;
159+
}
160+
}
161+
break;
162+
}
163+
};
164+
result
165+
}
166+
167+
/// Reads the specified number of bytes as a new byte array.
168+
fn read_bytes(ref self: Reader, num_bytes: usize) -> Result<ByteArray, felt252> {
169+
let mut array: Array<bytes31> = array![];
170+
let mut num_last_bytes = Option::None;
171+
let mut num_remaining_bytes = num_bytes;
172+
loop {
173+
let r = self.read_bytes_iteration(num_remaining_bytes, ref array);
174+
match r {
175+
Result::Ok((
176+
num_read, eof
177+
)) => {
178+
num_remaining_bytes -= num_read;
179+
if eof {
180+
num_last_bytes = Option::Some(Result::Ok(num_read));
181+
break;
182+
}
183+
},
184+
Result::Err(err) => {
185+
num_last_bytes = Option::Some(Result::Err(err));
186+
break;
187+
}
188+
}
189+
};
190+
// `num_last_bytes` is always set to Some before break.
191+
let num_last_bytes = num_last_bytes.unwrap()?;
192+
// num_last_bytes < 31
193+
let num_last_bytes = num_last_bytes.try_into().expect(UNEXPECTED_OVERFLOW);
194+
let array = ByteArrayImpl::new(array, num_last_bytes);
195+
Result::Ok(array)
196+
}
197+
198+
/// Returns number of remaining bytes to read.
199+
fn len(ref self: Reader) -> usize {
200+
let num_next_bytes = if self.next.is_some() {
201+
16
202+
} else {
203+
0
204+
};
205+
self.num_current_bytes.into() + num_next_bytes + self.array.len()
206+
}
207+
208+
/// Reads the specified number of bytes (up to 16) as a little endian unsigned integer.
209+
fn read_le(ref self: Reader, num_bytes: u8) -> Result<u128, felt252> {
210+
if num_bytes == 0 {
211+
return Result::Ok(0);
212+
}
213+
let value = u128_byte_reverse(self.read(num_bytes)?)
214+
/ one_shift_left_bytes_u128(16 - num_bytes);
215+
Result::Ok(value)
216+
}
217+
218+
/// Reads and hashes all the remaining data.
219+
fn keccak256(ref self: Reader) -> Result<u256, felt252> {
220+
let mut data: Array<u64> = array![];
221+
222+
let mut result = Result::Ok(());
223+
while self.len() >= 8 {
224+
match self.read_le(8) {
225+
Result::Ok(value) => { data.append(value.try_into().expect(UNEXPECTED_OVERFLOW)); },
226+
Result::Err(err) => {
227+
result = Result::Err(err);
228+
break;
229+
},
230+
}
231+
};
232+
result?;
233+
234+
let last_len = self.len();
235+
// last_len < 8
236+
let last = self.read_le(last_len.try_into().expect(UNEXPECTED_OVERFLOW))?;
237+
let last = last.try_into().expect(UNEXPECTED_OVERFLOW);
238+
let hash = cairo_keccak(ref data, last, last_len);
239+
Result::Ok(hash)
240+
}
241+
}
242+
243+
#[generate_trait]
244+
impl ReaderPrivateImpl of ReaderPrivateTrait {
245+
/// Reads the specified number of bytes from `self.current`.
246+
/// Panics if attempted to read more than `self.num_current_bytes`.
247+
fn read_from_current(ref self: Reader, num_bytes: u8) -> u128 {
248+
let num_remaining_bytes = self.num_current_bytes - num_bytes;
249+
let divisor = one_shift_left_bytes_u128(num_remaining_bytes);
250+
// divisor != 0
251+
let (high, low) = DivRem::div_rem(self.current, divisor.try_into().unwrap());
252+
self.current = low;
253+
self.num_current_bytes = num_remaining_bytes;
254+
high
255+
}
256+
257+
/// Replenishes `self.current` and `self.num_current_bytes`.
258+
/// This should only be called when all bytes from `self.current` has been read.
259+
/// Returns `EOF` error if no more data is available.
260+
fn fetch_next(ref self: Reader) -> Result<(), felt252> {
261+
match self.next {
262+
Option::Some(next) => {
263+
self.next = Option::None;
264+
self.current = next;
265+
self.num_current_bytes = 16;
266+
},
267+
Option::None => {
268+
let (value, bytes) = self.array.pop_front().ok_or(EOF)?;
269+
let value: u256 = value.into();
270+
if bytes > 16 {
271+
self.current = value.high;
272+
self.next = Option::Some(value.low);
273+
self.num_current_bytes = bytes - 16;
274+
} else {
275+
self.current = value.low;
276+
self.num_current_bytes = bytes;
277+
}
278+
},
279+
}
280+
Result::Ok(())
281+
}
282+
283+
// Moved out from `read_bytes` because we cannot use `return` or `?` within a loop.
284+
fn read_bytes_iteration(
285+
ref self: Reader, num_bytes: usize, ref array: Array<bytes31>
286+
) -> Result<(usize, bool), felt252> {
287+
if num_bytes >= 31 {
288+
let high = self.read(15)?;
289+
let low = self.read(16)?;
290+
let value: felt252 = u256 { high, low }.try_into().expect(UNEXPECTED_OVERFLOW);
291+
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
292+
Result::Ok((31, false))
293+
} else if num_bytes > 16 {
294+
// num_bytes < 31
295+
let high = self.read((num_bytes - 16).try_into().expect(UNEXPECTED_OVERFLOW))?;
296+
let low = self.read(16)?;
297+
let value: felt252 = u256 { high, low }.try_into().expect(UNEXPECTED_OVERFLOW);
298+
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
299+
Result::Ok((num_bytes, true))
300+
} else {
301+
// bytes < 16
302+
let low = self.read(num_bytes.try_into().expect(UNEXPECTED_OVERFLOW))?;
303+
let value: felt252 = low.try_into().expect(UNEXPECTED_OVERFLOW);
304+
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
305+
Result::Ok((num_bytes, true))
306+
}
307+
}
308+
}
309+
310+
// Returns 1 << (8 * `n_bytes`) as u128, where `n_bytes` must be < BYTES_IN_U128.
311+
//
312+
// Panics if `n_bytes >= 16`.
313+
fn one_shift_left_bytes_u128(n_bytes: u8) -> u128 {
314+
match n_bytes {
315+
0 => 0x1,
316+
1 => 0x100,
317+
2 => 0x10000,
318+
3 => 0x1000000,
319+
4 => 0x100000000,
320+
5 => 0x10000000000,
321+
6 => 0x1000000000000,
322+
7 => 0x100000000000000,
323+
8 => 0x10000000000000000,
324+
9 => 0x1000000000000000000,
325+
10 => 0x100000000000000000000,
326+
11 => 0x10000000000000000000000,
327+
12 => 0x1000000000000000000000000,
328+
13 => 0x100000000000000000000000000,
329+
14 => 0x10000000000000000000000000000,
330+
15 => 0x1000000000000000000000000000000,
331+
_ => core::panic_with_felt252('n_bytes too big'),
332+
}
333+
}

0 commit comments

Comments
 (0)