Skip to content

Commit 30c741e

Browse files
committed
feat(target_chains/starknet): add multi-purpose keccak hasher
1 parent 5fac32f commit 30c741e

File tree

5 files changed

+230
-134
lines changed

5 files changed

+230
-134
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use super::reader::{Reader, ReaderImpl};
2+
use core::cmp::min;
3+
use core::integer::u128_byte_reverse;
4+
use pyth::util::{
5+
ONE_SHIFT_160, UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, ONE_SHIFT_64, one_shift_left_bytes_u64,
6+
u64_byte_reverse,
7+
};
8+
9+
/// Allows to push data as big endian to a buffer and apply
10+
/// the keccak256 hash.
11+
#[derive(Drop, Debug)]
12+
pub struct Hasher {
13+
// Inputs in little endian.
14+
inputs_le: Array<u64>,
15+
// Last pushed bytes in big endian.
16+
last_be: u64,
17+
// Number of filled bytes in `self.last_be`.
18+
num_last_bytes: u8,
19+
}
20+
21+
#[generate_trait]
22+
pub impl HasherImpl of HasherTrait {
23+
/// Creates an empty hasher.
24+
fn new() -> Hasher {
25+
Hasher { inputs_le: array![], last_be: 0, num_last_bytes: 0 }
26+
}
27+
28+
fn push_u8(ref self: Hasher, value: u8) {
29+
self.push_to_last(value.into(), 1);
30+
}
31+
fn push_u16(ref self: Hasher, value: u16) {
32+
self.push_num_bytes(value.into(), 2);
33+
}
34+
fn push_u32(ref self: Hasher, value: u32) {
35+
self.push_num_bytes(value.into(), 4);
36+
}
37+
fn push_u64(ref self: Hasher, value: u64) {
38+
self.push_num_bytes(value, 8);
39+
}
40+
fn push_u128(ref self: Hasher, value: u128) {
41+
let divisor = ONE_SHIFT_64.try_into().expect(UNEXPECTED_ZERO);
42+
let (high, low) = DivRem::div_rem(value, divisor);
43+
self.push_u64(high.try_into().expect(UNEXPECTED_OVERFLOW));
44+
self.push_u64(low.try_into().expect(UNEXPECTED_OVERFLOW));
45+
}
46+
fn push_u160(ref self: Hasher, value: u256) {
47+
assert!(value / ONE_SHIFT_160 == 0, "u160 value too big");
48+
self.push_num_bytes(value.high.try_into().expect(UNEXPECTED_OVERFLOW), 4);
49+
self.push_u128(value.low);
50+
}
51+
fn push_u256(ref self: Hasher, value: u256) {
52+
self.push_u128(value.high);
53+
self.push_u128(value.low);
54+
}
55+
56+
/// Reads all remaining data from the reader and pushes it to
57+
/// the hashing buffer.
58+
fn push_reader(ref self: Hasher, ref reader: Reader) -> Result<(), felt252> {
59+
let mut result = Result::Ok(());
60+
while reader.len() > 0 {
61+
let mut chunk_len = 8 - self.num_last_bytes;
62+
if reader.len() < chunk_len.into() {
63+
// reader.len() < 8
64+
chunk_len = reader.len().try_into().expect(UNEXPECTED_OVERFLOW);
65+
}
66+
match reader.read_num_bytes(chunk_len) {
67+
Result::Ok(value) => {
68+
// chunk_len <= 8 so value must fit in u64.
69+
self.push_to_last(value.try_into().expect(UNEXPECTED_OVERFLOW), chunk_len);
70+
},
71+
Result::Err(err) => {
72+
result = Result::Err(err);
73+
break;
74+
},
75+
}
76+
};
77+
result
78+
}
79+
80+
/// Returns the keccak256 hash of the buffer. The output hash is interpreted
81+
/// as a big endian unsigned integer.
82+
fn finalize(ref self: Hasher) -> u256 {
83+
let last_le = if self.num_last_bytes == 0 {
84+
0
85+
} else {
86+
u64_byte_reverse(self.last_be) / one_shift_left_bytes_u64(8 - self.num_last_bytes)
87+
};
88+
let hash_le = core::keccak::cairo_keccak(
89+
ref self.inputs_le, last_le, self.num_last_bytes.into()
90+
);
91+
u256 { low: u128_byte_reverse(hash_le.high), high: u128_byte_reverse(hash_le.low), }
92+
}
93+
}
94+
95+
#[generate_trait]
96+
impl HasherPrivateImpl of HasherPrivateTrait {
97+
// Adds specified number of bytes to the buffer.
98+
fn push_num_bytes(ref self: Hasher, value: u64, num_bytes: u8) {
99+
assert!(num_bytes <= 8, "num_bytes too high in Hasher::push_num_bytes");
100+
let num_high_bytes = min(num_bytes, 8 - self.num_last_bytes);
101+
let num_low_bytes = num_bytes - num_high_bytes;
102+
let divisor = one_shift_left_bytes_u64(num_low_bytes).try_into().expect(UNEXPECTED_ZERO);
103+
let (high, low) = DivRem::div_rem(value, divisor);
104+
self.push_to_last(high, num_high_bytes);
105+
self.push_to_last(low, num_low_bytes);
106+
}
107+
108+
fn push_to_last(ref self: Hasher, value: u64, num_bytes: u8) {
109+
assert!(num_bytes <= 8 - self.num_last_bytes, "num_bytes too high in Hasher::push_to_last");
110+
if num_bytes == 8 {
111+
self.last_be = value;
112+
} else {
113+
self.last_be = self.last_be * one_shift_left_bytes_u64(num_bytes) + value;
114+
};
115+
self.num_last_bytes += num_bytes;
116+
if self.num_last_bytes == 8 {
117+
self.inputs_le.append(u64_byte_reverse(self.last_be));
118+
self.last_be = 0;
119+
self.num_last_bytes = 0;
120+
}
121+
}
122+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
pub mod pyth;
22
pub mod wormhole;
33
pub mod reader;
4+
pub mod hash;
5+
mod util;

target_chains/starknet/contracts/src/reader.cairo

Lines changed: 35 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use core::array::ArrayTrait;
33
use core::keccak::cairo_keccak;
44
use core::integer::u128_byte_reverse;
55
use core::fmt::{Debug, Formatter};
6+
use pyth::util::{UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, one_shift_left_bytes_u128};
67

7-
pub const EOF: felt252 = 'unexpected end of input';
8-
pub const UNEXPECTED_OVERFLOW: felt252 = 'unexpected overflow';
8+
pub mod error_codes {
9+
pub const EOF: felt252 = 'unexpected end of input';
10+
}
911

1012
/// A byte array with storage format similar to `core::ByteArray`, but
1113
/// suitable for reading data from it.
@@ -70,8 +72,7 @@ pub impl ByteArrayImpl of ByteArrayTrait {
7072
}
7173
}
7274

73-
/// Allows to read data from a byte array.
74-
/// Uses big endian unless specified otherwise.
75+
/// Allows to read data from a byte array as big endian integers.
7576
/// All methods return `EOF` error if attempted to
7677
/// read more bytes than is available.
7778
#[derive(Drop, Clone)]
@@ -94,15 +95,16 @@ pub impl ReaderImpl of ReaderTrait {
9495
}
9596

9697
/// 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+
fn read_num_bytes(ref self: Reader, num_bytes: u8) -> Result<u128, felt252> {
99+
assert!(num_bytes <= 16, "Reader::read_num_bytes: num_bytes is too large");
98100
if num_bytes <= self.num_current_bytes {
99101
let x = self.read_from_current(num_bytes);
100102
return Result::Ok(x);
101103
}
102104
let num_low_bytes = num_bytes - self.num_current_bytes;
103105
let high = self.current;
104106
self.fetch_next()?;
105-
let low = self.read(num_low_bytes)?;
107+
let low = self.read_num_bytes(num_low_bytes)?;
106108
let value = if num_low_bytes == 16 {
107109
low
108110
} else {
@@ -112,28 +114,34 @@ pub impl ReaderImpl of ReaderTrait {
112114
}
113115

114116
fn read_u256(ref self: Reader) -> Result<u256, felt252> {
115-
let high = self.read(16)?;
116-
let low = self.read(16)?;
117+
let high = self.read_num_bytes(16)?;
118+
let low = self.read_num_bytes(16)?;
119+
let value = u256 { high, low };
120+
Result::Ok(value)
121+
}
122+
fn read_u160(ref self: Reader) -> Result<u256, felt252> {
123+
let high = self.read_num_bytes(4)?;
124+
let low = self.read_num_bytes(16)?;
117125
let value = u256 { high, low };
118126
Result::Ok(value)
119127
}
120128
fn read_u128(ref self: Reader) -> Result<u128, felt252> {
121-
self.read(16)
129+
self.read_num_bytes(16)
122130
}
123131
fn read_u64(ref self: Reader) -> Result<u64, felt252> {
124-
let value = self.read(8)?.try_into().expect(UNEXPECTED_OVERFLOW);
132+
let value = self.read_num_bytes(8)?.try_into().expect(UNEXPECTED_OVERFLOW);
125133
Result::Ok(value)
126134
}
127135
fn read_u32(ref self: Reader) -> Result<u32, felt252> {
128-
let value = self.read(4)?.try_into().expect(UNEXPECTED_OVERFLOW);
136+
let value = self.read_num_bytes(4)?.try_into().expect(UNEXPECTED_OVERFLOW);
129137
Result::Ok(value)
130138
}
131139
fn read_u16(ref self: Reader) -> Result<u16, felt252> {
132-
let value = self.read(2)?.try_into().expect(UNEXPECTED_OVERFLOW);
140+
let value = self.read_num_bytes(2)?.try_into().expect(UNEXPECTED_OVERFLOW);
133141
Result::Ok(value)
134142
}
135143
fn read_u8(ref self: Reader) -> Result<u8, felt252> {
136-
let value = self.read(1)?.try_into().expect(UNEXPECTED_OVERFLOW);
144+
let value = self.read_num_bytes(1)?.try_into().expect(UNEXPECTED_OVERFLOW);
137145
Result::Ok(value)
138146
}
139147

@@ -142,7 +150,7 @@ pub impl ReaderImpl of ReaderTrait {
142150
let mut result = Result::Ok(());
143151
while num_bytes > 0 {
144152
if num_bytes > 16 {
145-
match self.read(16) {
153+
match self.read_num_bytes(16) {
146154
Result::Ok(_) => {},
147155
Result::Err(err) => {
148156
result = Result::Err(err);
@@ -151,7 +159,7 @@ pub impl ReaderImpl of ReaderTrait {
151159
}
152160
num_bytes -= 16;
153161
} else {
154-
match self.read(num_bytes) {
162+
match self.read_num_bytes(num_bytes) {
155163
Result::Ok(_) => {},
156164
Result::Err(err) => {
157165
result = Result::Err(err);
@@ -165,7 +173,7 @@ pub impl ReaderImpl of ReaderTrait {
165173
}
166174

167175
/// Reads the specified number of bytes as a new byte array.
168-
fn read_bytes(ref self: Reader, num_bytes: usize) -> Result<ByteArray, felt252> {
176+
fn read_byte_array(ref self: Reader, num_bytes: usize) -> Result<ByteArray, felt252> {
169177
let mut array: Array<bytes31> = array![];
170178
let mut num_last_bytes = Option::None;
171179
let mut num_remaining_bytes = num_bytes;
@@ -204,40 +212,6 @@ pub impl ReaderImpl of ReaderTrait {
204212
};
205213
self.num_current_bytes.into() + num_next_bytes + self.array.len()
206214
}
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-
}
241215
}
242216

243217
#[generate_trait]
@@ -246,9 +220,10 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
246220
/// Panics if attempted to read more than `self.num_current_bytes`.
247221
fn read_from_current(ref self: Reader, num_bytes: u8) -> u128 {
248222
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());
223+
let divisor = one_shift_left_bytes_u128(num_remaining_bytes)
224+
.try_into()
225+
.expect(UNEXPECTED_ZERO);
226+
let (high, low) = DivRem::div_rem(self.current, divisor);
252227
self.current = low;
253228
self.num_current_bytes = num_remaining_bytes;
254229
high
@@ -265,7 +240,7 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
265240
self.num_current_bytes = 16;
266241
},
267242
Option::None => {
268-
let (value, bytes) = self.array.pop_front().ok_or(EOF)?;
243+
let (value, bytes) = self.array.pop_front().ok_or(error_codes::EOF)?;
269244
let value: u256 = value.into();
270245
if bytes > 16 {
271246
self.current = value.high;
@@ -285,49 +260,25 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
285260
ref self: Reader, num_bytes: usize, ref array: Array<bytes31>
286261
) -> Result<(usize, bool), felt252> {
287262
if num_bytes >= 31 {
288-
let high = self.read(15)?;
289-
let low = self.read(16)?;
263+
let high = self.read_num_bytes(15)?;
264+
let low = self.read_num_bytes(16)?;
290265
let value: felt252 = u256 { high, low }.try_into().expect(UNEXPECTED_OVERFLOW);
291266
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
292267
Result::Ok((31, false))
293268
} else if num_bytes > 16 {
294269
// num_bytes < 31
295-
let high = self.read((num_bytes - 16).try_into().expect(UNEXPECTED_OVERFLOW))?;
296-
let low = self.read(16)?;
270+
let high = self
271+
.read_num_bytes((num_bytes - 16).try_into().expect(UNEXPECTED_OVERFLOW))?;
272+
let low = self.read_num_bytes(16)?;
297273
let value: felt252 = u256 { high, low }.try_into().expect(UNEXPECTED_OVERFLOW);
298274
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
299275
Result::Ok((num_bytes, true))
300276
} else {
301277
// bytes < 16
302-
let low = self.read(num_bytes.try_into().expect(UNEXPECTED_OVERFLOW))?;
278+
let low = self.read_num_bytes(num_bytes.try_into().expect(UNEXPECTED_OVERFLOW))?;
303279
let value: felt252 = low.try_into().expect(UNEXPECTED_OVERFLOW);
304280
array.append(value.try_into().expect(UNEXPECTED_OVERFLOW));
305281
Result::Ok((num_bytes, true))
306282
}
307283
}
308284
}
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)