Skip to content

Commit 8036121

Browse files
authored
fix: review comments from #25 (#86)
1 parent 0723ded commit 8036121

9 files changed

Lines changed: 87 additions & 116 deletions

File tree

crates/charon-cluster/src/definition.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,11 @@ impl Definition {
324324
};
325325

326326
def.validator_addresses = fee_recipient_addresses
327-
.into_iter()
328-
.enumerate()
329-
.map(|(i, address)| ValidatorAddresses {
330-
fee_recipient_address: address,
331-
withdrawal_address: withdrawal_addresses[i].clone(),
327+
.iter()
328+
.zip(withdrawal_addresses.iter())
329+
.map(|(fee, wa)| ValidatorAddresses {
330+
fee_recipient_address: fee.clone(),
331+
withdrawal_address: wa.clone(),
332332
})
333333
.collect();
334334

crates/charon-cluster/src/eip712sigs.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,26 +134,28 @@ fn digest_eip712(
134134
) -> Result<Vec<u8>> {
135135
let chain_id = fork_version_to_chain_id(definition.fork_version.as_ref())?;
136136

137-
let mut data = TypedData {
137+
let fields = typ
138+
.fields
139+
.iter()
140+
.map(|f| Field {
141+
name: f.field.to_string(),
142+
field_type: f.field_type.to_string(),
143+
value: (f.value_func)(definition, operator),
144+
})
145+
.collect::<Vec<_>>();
146+
147+
let data = TypedData {
138148
domain: Domain {
139149
name: "Obol".to_string(),
140150
version: "1".to_string(),
141151
chain_id,
142152
},
143153
primary_type: Type {
144154
name: typ.primary_type.to_string(),
145-
fields: vec![],
155+
fields,
146156
},
147157
};
148158

149-
for field in typ.fields.iter() {
150-
data.primary_type.fields.push(Field {
151-
name: field.field.to_string(),
152-
field_type: field.field_type.to_string(),
153-
value: (field.value_func)(definition, operator),
154-
});
155-
}
156-
157159
let digest = hash_typed_data(&data).map_err(EIP712Error::FailedToHashTypedData)?;
158160

159161
Ok(digest)

crates/charon-eth2/src/eip712.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ pub struct Field {
3838
/// Name of the field.
3939
pub name: String,
4040
/// Type of the field.
41-
pub field_type: String,
41+
pub field_type: Primitive,
4242
/// Value of the field.
4343
pub value: Value,
4444
}
4545

4646
impl Field {
4747
/// Creates a new field.
48-
pub fn new(name: String, field_type: String, value: Value) -> Self {
48+
pub fn new(name: impl Into<String>, field_type: impl Into<String>, value: Value) -> Self {
4949
Self {
50-
name,
51-
field_type,
50+
name: name.into(),
51+
field_type: field_type.into(),
5252
value,
5353
}
5454
}
@@ -156,9 +156,9 @@ fn encode_field(field: &Field) -> Result<Vec<u8>> {
156156
match (field.field_type.as_str(), &field.value) {
157157
(PRIMITIVE_STRING, Value::String(s)) => Ok(keccak_hash(s.as_bytes())?),
158158
(PRIMITIVE_UINT256, Value::Number(n)) => {
159-
let mut bytes = [0u8; 32];
159+
let mut bytes = vec![0u8; 32];
160160
bytes[24..].copy_from_slice(&n.to_be_bytes());
161-
Ok(bytes.to_vec())
161+
Ok(bytes)
162162
}
163163
_ => Err(Eip712Error::UnsupportedFieldType {
164164
field_type: field.field_type.clone(),

crates/charon-eth2/src/enr.rs

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use charon_k1util::{self, self as k1util, SIGNATURE_LEN_WITHOUT_V};
55
use k256::{PublicKey, SecretKey, elliptic_curve};
66
use sha3::{Digest, Keccak256};
77

8-
use crate::rlp::{RlpError, decode_bytes_list, encode_bytes_list};
8+
use crate::{
9+
rlp::{RlpError, decode_bytes_list, encode_bytes_list},
10+
utils,
11+
};
912

1013
/// The key for the secp256k1 public key in the ENR.
1114
pub const KEY_SECP256K1: &str = "secp256k1";
@@ -154,45 +157,27 @@ impl Record {
154157
///
155158
/// Returns None if the IP address is not set.
156159
pub fn ip(&self) -> Option<Ipv4Addr> {
157-
self.kvs.get(KEY_IP).and_then(|value| {
158-
if value.len() != 4 {
159-
return None;
160-
}
161-
162-
let mut bytes = [0u8; 4];
163-
bytes.copy_from_slice(value);
164-
Some(Ipv4Addr::from(bytes))
165-
})
160+
let value = self.kvs.get(KEY_IP)?;
161+
let bytes: [u8; 4] = value.as_slice().try_into().ok()?;
162+
Some(Ipv4Addr::from(bytes))
166163
}
167164

168165
/// Returns the TCP port of the record.
169166
///
170167
/// Returns None if the TCP port is not set.
171168
pub fn tcp(&self) -> Option<u16> {
172-
self.kvs.get(KEY_TCP).and_then(|value| {
173-
if value.len() != 2 {
174-
return None;
175-
}
176-
177-
let mut bytes = [0u8; 2];
178-
bytes.copy_from_slice(value);
179-
Some(u16::from_be_bytes(bytes))
180-
})
169+
let value = self.kvs.get(KEY_TCP)?;
170+
let bytes = value.as_slice().try_into().ok()?;
171+
Some(u16::from_be_bytes(bytes))
181172
}
182173

183174
/// Returns the UDP port of the record.
184175
///
185176
/// Returns None if the UDP port is not set.
186177
pub fn udp(&self) -> Option<u16> {
187-
self.kvs.get(KEY_UDP).and_then(|value| {
188-
if value.len() != 2 {
189-
return None;
190-
}
191-
192-
let mut bytes = [0u8; 2];
193-
bytes.copy_from_slice(value);
194-
Some(u16::from_be_bytes(bytes))
195-
})
178+
let value = self.kvs.get(KEY_UDP)?;
179+
let bytes = value.as_slice().try_into().ok()?;
180+
Some(u16::from_be_bytes(bytes))
196181
}
197182
}
198183

@@ -249,9 +234,11 @@ impl TryFrom<&str> for Record {
249234
kvs: HashMap::new(),
250235
};
251236

252-
for i in (2..elements.len()).step_by(2) {
253-
let key = String::from_utf8_lossy(&elements[i]).to_string();
254-
let value = elements[i.wrapping_add(1)].clone();
237+
for pair in elements.chunks_exact(2).skip(1) {
238+
let [key, value] = pair else {
239+
unreachable!("Expected even number of elements");
240+
};
241+
let key = String::from_utf8_lossy(key).to_string();
255242

256243
if record.kvs.contains_key(&key) {
257244
return Err(RecordError::DuplicateKey(key));
@@ -262,11 +249,11 @@ impl TryFrom<&str> for Record {
262249
match key.as_str() {
263250
KEY_SECP256K1 => {
264251
record.public_key = Some(
265-
PublicKey::from_sec1_bytes(&value).map_err(RecordError::Secp256k1Error)?,
252+
PublicKey::from_sec1_bytes(value).map_err(RecordError::Secp256k1Error)?,
266253
);
267254
}
268255
KEY_ID => {
269-
let value_str = String::from_utf8_lossy(&value).to_string();
256+
let value_str = String::from_utf8_lossy(value).to_string();
270257
if value_str != VAL_ID {
271258
return Err(RecordError::InvalidFormat(
272259
InvalidFormatError::NonV4IdentitySchemeNotSupported,
@@ -277,21 +264,15 @@ impl TryFrom<&str> for Record {
277264
}
278265
}
279266

280-
if record.public_key.is_none() {
267+
let Some(public_key) = record.public_key else {
281268
return Err(RecordError::InvalidFormat(
282269
InvalidFormatError::PublicKeyNotSet,
283270
));
284-
}
271+
};
285272

286273
let encoded_elements = encode_bytes_list(&elements[1..]);
287274

288-
verify(
289-
&record
290-
.public_key
291-
.unwrap_or_else(|| unreachable!("Public key expected to be set")),
292-
&record.signature,
293-
&encoded_elements,
294-
)?;
275+
verify(&public_key, &record.signature, &encoded_elements)?;
295276

296277
Ok(record)
297278
}
@@ -307,8 +288,9 @@ pub(crate) fn sign(
307288
let digest = hasher.finalize();
308289

309290
let signature = k1util::sign(private_key, &digest).map_err(RecordError::FailedToSign)?;
310-
let mut signature_without_v = [0u8; SIGNATURE_LEN_WITHOUT_V];
311-
signature_without_v.copy_from_slice(&signature[..SIGNATURE_LEN_WITHOUT_V]);
291+
let signature_without_v = signature[..SIGNATURE_LEN_WITHOUT_V]
292+
.try_into()
293+
.expect("SIGNATURE_LEN_WITHOUT_V < SIGNATURE_LEN");
312294

313295
Ok(signature_without_v)
314296
}
@@ -335,7 +317,7 @@ pub(crate) fn encode_elements(signature: &[u8], kvs: &HashMap<String, Vec<u8>>)
335317
keys.sort();
336318

337319
// Start with sequence number = 0
338-
let mut elements: Vec<Vec<u8>> = vec![to_big_endian(0)];
320+
let mut elements: Vec<Vec<u8>> = vec![utils::to_big_endian(0)];
339321

340322
for key in keys {
341323
elements.push(key.as_bytes().to_vec());
@@ -349,18 +331,9 @@ pub(crate) fn encode_elements(signature: &[u8], kvs: &HashMap<String, Vec<u8>>)
349331
encode_bytes_list(&elements)
350332
}
351333

352-
/// Converts an integer to big-endian bytes without leading zeros.
353-
fn to_big_endian(mut n: u64) -> Vec<u8> {
354-
let mut result = Vec::new();
355-
while n > 0 {
356-
result.insert(0, (n & 0xFF) as u8);
357-
n >>= 8;
358-
}
359-
result
360-
}
361-
362334
#[cfg(test)]
363335
mod tests {
336+
use crate::utils;
364337
use charon_testutil::random::generate_insecure_k1_key;
365338
use k256::{
366339
Secp256k1,
@@ -491,7 +464,7 @@ mod tests {
491464
keys.sort();
492465

493466
// Start with sequence number = 0
494-
let mut elements: Vec<Vec<u8>> = vec![to_big_endian(0), to_big_endian(0)];
467+
let mut elements: Vec<Vec<u8>> = vec![utils::to_big_endian(0), utils::to_big_endian(0)];
495468

496469
for key in keys {
497470
elements.push(key.as_bytes().to_vec());

crates/charon-eth2/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ pub mod eip712;
1515

1616
/// Network utilities.
1717
pub mod network;
18+
19+
/// Utilities.
20+
pub(crate) mod utils;

crates/charon-eth2/src/rlp.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use thiserror::Error;
22

3+
use crate::utils;
4+
35
/// An error that can occur when decoding or encoding RLP data.
46
#[derive(Debug, Error)]
57
pub enum RlpError {
@@ -132,7 +134,7 @@ fn encode_length(length: usize, offset: u8) -> Vec<u8> {
132134
];
133135
}
134136

135-
let big_endian = to_big_endian(length);
137+
let big_endian = utils::to_big_endian(length);
136138
let prefix = u8::try_from(big_endian.len())
137139
.expect("big_endian.len() is always less than 256")
138140
.wrapping_add(offset)
@@ -141,17 +143,6 @@ fn encode_length(length: usize, offset: u8) -> Vec<u8> {
141143
[vec![prefix], big_endian].concat()
142144
}
143145

144-
/// Converts an integer to big-endian byte representation without leading zeros.
145-
fn to_big_endian(mut value: usize) -> Vec<u8> {
146-
let mut bytes = Vec::new();
147-
while value > 0 {
148-
bytes.push(u8::try_from(value % 256).expect("value % 256 is always less than 256"));
149-
value >>= 8;
150-
}
151-
bytes.reverse();
152-
bytes
153-
}
154-
155146
/// Decodes a big-endian integer from a byte slice at the given offset and
156147
/// length.
157148
fn from_big_endian(bytes: &[u8], offset: usize, length: usize) -> Result<usize> {

crates/charon-eth2/src/utils.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/// Converts an integer to big-endian byte representation without leading zeros.
2+
pub(crate) fn to_big_endian(value: usize) -> Vec<u8> {
3+
value
4+
.to_be_bytes()
5+
.into_iter()
6+
.skip_while(|x| *x == 0)
7+
.collect()
8+
}

crates/charon-k1util/src/k1util.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ pub enum K1UtilError {
4141
InvalidSignature(ecdsa::Error),
4242

4343
/// The hash length is invalid.
44-
#[error("The hash length is invalid: expected {expected}, actual {actual}")]
44+
#[error("The hash length is invalid: expected {K1_HASH_LEN}, actual {actual}")]
4545
InvalidHashLength {
46-
/// The expected hash length.
47-
expected: usize,
4846
/// The actual hash length.
4947
actual: usize,
5048
},
@@ -95,10 +93,7 @@ pub fn public_key_from_libp2p(pk: &Libp2pPublicKey) -> Result<PublicKey> {
9593
/// or 1.
9694
pub fn sign(key: &SecretKey, hash: &[u8]) -> Result<[u8; SIGNATURE_LEN]> {
9795
if hash.len() != K1_HASH_LEN {
98-
return Err(K1UtilError::InvalidHashLength {
99-
expected: K1_HASH_LEN,
100-
actual: hash.len(),
101-
});
96+
return Err(K1UtilError::InvalidHashLength { actual: hash.len() });
10297
}
10398

10499
let mut hash_bytes = [0u8; K1_HASH_LEN];
@@ -142,10 +137,7 @@ pub fn verify_64(pubkey: &PublicKey, hash: &[u8], sig: &[u8]) -> Result<bool> {
142137
}
143138

144139
if hash.len() != K1_HASH_LEN {
145-
return Err(K1UtilError::InvalidHashLength {
146-
expected: K1_HASH_LEN,
147-
actual: hash.len(),
148-
});
140+
return Err(K1UtilError::InvalidHashLength { actual: hash.len() });
149141
}
150142

151143
let signature = Signature::from_slice(sig).map_err(K1UtilError::InvalidSignature)?;
@@ -163,10 +155,7 @@ pub fn verify_64(pubkey: &PublicKey, hash: &[u8], sig: &[u8]) -> Result<bool> {
163155
/// Recover recovers the public key from a signature.
164156
pub fn recover(hash: &[u8], sig: &[u8]) -> Result<PublicKey> {
165157
if hash.len() != K1_HASH_LEN {
166-
return Err(K1UtilError::InvalidHashLength {
167-
expected: K1_HASH_LEN,
168-
actual: hash.len(),
169-
});
158+
return Err(K1UtilError::InvalidHashLength { actual: hash.len() });
170159
}
171160

172161
if sig.len() != SIGNATURE_LEN {
@@ -176,7 +165,11 @@ pub fn recover(hash: &[u8], sig: &[u8]) -> Result<PublicKey> {
176165
});
177166
}
178167

179-
let recovery_byte = sig[K1_REC_IDX];
168+
let mut recovery_byte = sig[K1_REC_IDX];
169+
170+
if recovery_byte == 27 || recovery_byte == 28 {
171+
recovery_byte = recovery_byte.wrapping_sub(27);
172+
}
180173

181174
let signature =
182175
Signature::from_slice(&sig[..SIGNATURE_LEN - 1]).map_err(K1UtilError::InvalidSignature)?;

0 commit comments

Comments
 (0)