Skip to content

Commit c3c48e2

Browse files
committed
refactor: exit models
1 parent 225250d commit c3c48e2

1 file changed

Lines changed: 93 additions & 149 deletions

File tree

crates/charon/src/obolapi/exit.rs

Lines changed: 93 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,59 @@ use eth2api::types::{
1616
GetPoolVoluntaryExitsResponseResponseDatum, Phase0SignedVoluntaryExitMessage,
1717
};
1818

19-
/// Type alias for signed voluntary exit from eth2api.
20-
pub type SignedVoluntaryExit = GetPoolVoluntaryExitsResponseResponseDatum;
21-
2219
use crate::obolapi::{
2320
client::Client,
2421
error::{Error, Result},
2522
helper::{bearer_string, from_0x, to_0x},
2623
};
2724

25+
/// Type alias for signed voluntary exit from eth2api.
26+
pub type SignedVoluntaryExit = GetPoolVoluntaryExitsResponseResponseDatum;
27+
28+
/// Trait for types that can be hashed using SSZ hash tree root.
29+
pub trait SszHashable {
30+
/// Hashes this value into the provided hasher.
31+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()>;
32+
33+
/// Computes the SSZ hash tree root of this value.
34+
fn hash_tree_root(&self) -> Result<[u8; 32]> {
35+
let mut hh = Hasher::default();
36+
self.hash_with(&mut hh)?;
37+
hh.hash_root().map_err(map_hasher_error)
38+
}
39+
}
40+
41+
impl SszHashable for SignedVoluntaryExit {
42+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
43+
let index = hh.index();
44+
45+
self.message.hash_with(hh)?;
46+
let sig_bytes = from_0x(&self.signature, SSZ_LEN_BLS_SIG)?;
47+
put_bytes_n(hh, &sig_bytes, SSZ_LEN_BLS_SIG)?;
48+
49+
hh.merkleize(index).map_err(map_walker_error)?;
50+
Ok(())
51+
}
52+
}
53+
54+
impl SszHashable for Phase0SignedVoluntaryExitMessage {
55+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
56+
let index = hh.index();
57+
58+
let epoch = self.epoch.parse::<u64>().map_err(Error::EpochParse)?;
59+
let validator_index = self
60+
.validator_index
61+
.parse::<u64>()
62+
.map_err(Error::EpochParse)?;
63+
64+
hh.put_uint64(epoch).map_err(map_walker_error)?;
65+
hh.put_uint64(validator_index).map_err(map_walker_error)?;
66+
67+
hh.merkleize(index).map_err(map_walker_error)?;
68+
Ok(())
69+
}
70+
}
71+
2872
/// An exit message alongside its BLS12-381 hex-encoded signature.
2973
#[derive(Debug, Clone, Serialize, Deserialize)]
3074
pub struct ExitBlob {
@@ -36,10 +80,23 @@ pub struct ExitBlob {
3680
pub signed_exit_message: SignedVoluntaryExit,
3781
}
3882

39-
impl ExitBlob {
40-
/// Computes the SSZ hash tree root of this ExitBlob.
41-
pub fn hash_tree_root(&self) -> Result<[u8; 32]> {
42-
hash_exit_blob(self)
83+
impl SszHashable for ExitBlob {
84+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
85+
let index = hh.index();
86+
87+
let pk = self.public_key.as_ref().ok_or_else(|| {
88+
use charon_cluster::ssz::SSZError;
89+
Error::Ssz(SSZError::UnsupportedVersion(
90+
"missing public key".to_string(),
91+
))
92+
})?;
93+
let pk_bytes = from_0x(pk, SSZ_LEN_PUB_KEY)?;
94+
hh.put_bytes(&pk_bytes).map_err(map_walker_error)?;
95+
96+
self.signed_exit_message.hash_with(hh)?;
97+
98+
hh.merkleize(index).map_err(map_walker_error)?;
99+
Ok(())
43100
}
44101
}
45102

@@ -48,24 +105,18 @@ impl ExitBlob {
48105
#[serde(transparent)]
49106
pub struct PartialExits(pub Vec<ExitBlob>);
50107

51-
impl PartialExits {
52-
/// Computes the SSZ hash tree root of the partial exits list.
53-
pub fn hash_tree_root(&self) -> Result<[u8; 32]> {
54-
hash_partial_exits(&self.0)
55-
}
56-
}
57-
58-
impl std::ops::Deref for PartialExits {
59-
type Target = Vec<ExitBlob>;
108+
impl SszHashable for PartialExits {
109+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
110+
let index = hh.index();
111+
let num = self.0.len();
60112

61-
fn deref(&self) -> &Self::Target {
62-
&self.0
63-
}
64-
}
113+
for exit_blob in &self.0 {
114+
exit_blob.hash_with(hh)?;
115+
}
65116

66-
impl std::ops::DerefMut for PartialExits {
67-
fn deref_mut(&mut self) -> &mut Self::Target {
68-
&mut self.0
117+
hh.merkleize_with_mixin(index, num, SSZ_MAX_EXITS)
118+
.map_err(map_walker_error)?;
119+
Ok(())
69120
}
70121
}
71122

@@ -75,12 +126,6 @@ impl From<Vec<ExitBlob>> for PartialExits {
75126
}
76127
}
77128

78-
impl From<PartialExits> for Vec<ExitBlob> {
79-
fn from(p: PartialExits) -> Self {
80-
p.0
81-
}
82-
}
83-
84129
/// An unsigned blob of data sent to the Obol API server, which is stored in the
85130
/// backend awaiting aggregation.
86131
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -93,10 +138,15 @@ pub struct UnsignedPartialExitRequest {
93138
pub share_idx: u64,
94139
}
95140

96-
impl UnsignedPartialExitRequest {
97-
/// Computes the SSZ hash tree root of this UnsignedPartialExitRequest.
98-
pub fn hash_tree_root(&self) -> Result<[u8; 32]> {
99-
hash_unsigned_partial_exit_request(self)
141+
impl SszHashable for UnsignedPartialExitRequest {
142+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
143+
let index = hh.index();
144+
145+
self.partial_exits.hash_with(hh)?;
146+
hh.put_uint64(self.share_idx).map_err(map_walker_error)?;
147+
148+
hh.merkleize(index).map_err(map_walker_error)?;
149+
Ok(())
100150
}
101151
}
102152

@@ -181,10 +231,16 @@ pub struct FullExitAuthBlob {
181231
pub share_index: u64,
182232
}
183233

184-
impl FullExitAuthBlob {
185-
/// Computes the SSZ hash tree root of this FullExitAuthBlob.
186-
pub fn hash_tree_root(&self) -> Result<[u8; 32]> {
187-
hash_full_exit_auth_blob(self)
234+
impl SszHashable for FullExitAuthBlob {
235+
fn hash_with<H: HashWalker>(&self, hh: &mut H) -> Result<()> {
236+
let index = hh.index();
237+
238+
hh.put_bytes(&self.lock_hash).map_err(map_walker_error)?;
239+
put_bytes_n(hh, &self.validator_pubkey, SSZ_LEN_PUB_KEY)?;
240+
hh.put_uint64(self.share_index).map_err(map_walker_error)?;
241+
242+
hh.merkleize(index).map_err(map_walker_error)?;
243+
Ok(())
188244
}
189245
}
190246
const SSZ_MAX_EXITS: usize = 65536;
@@ -406,118 +462,6 @@ fn put_bytes_n<H: HashWalker>(hh: &mut H, bytes: &[u8], expected_len: usize) ->
406462
hh.put_bytes(&padded).map_err(map_walker_error)
407463
}
408464

409-
fn hash_exit_blob(blob: &ExitBlob) -> Result<[u8; 32]> {
410-
let mut hh = Hasher::default();
411-
hash_exit_blob_with(blob, &mut hh)?;
412-
hh.hash_root().map_err(map_hasher_error)
413-
}
414-
415-
fn hash_exit_blob_with<H: HashWalker>(blob: &ExitBlob, hh: &mut H) -> Result<()> {
416-
let index = hh.index();
417-
418-
let pk = blob.public_key.as_ref().ok_or_else(|| {
419-
use charon_cluster::ssz::SSZError;
420-
Error::Ssz(SSZError::UnsupportedVersion(
421-
"missing public key".to_string(),
422-
))
423-
})?;
424-
let pk_bytes = from_0x(pk, SSZ_LEN_PUB_KEY)?;
425-
hh.put_bytes(&pk_bytes).map_err(map_walker_error)?;
426-
427-
hash_signed_voluntary_exit_with(&blob.signed_exit_message, hh)?;
428-
429-
hh.merkleize(index).map_err(map_walker_error)?;
430-
Ok(())
431-
}
432-
433-
fn hash_partial_exits(exits: &[ExitBlob]) -> Result<[u8; 32]> {
434-
let mut hh = Hasher::default();
435-
hash_partial_exits_with(exits, &mut hh)?;
436-
hh.hash_root().map_err(map_hasher_error)
437-
}
438-
439-
fn hash_unsigned_partial_exit_request(req: &UnsignedPartialExitRequest) -> Result<[u8; 32]> {
440-
let mut hh = Hasher::default();
441-
hash_unsigned_partial_exit_request_with(req, &mut hh)?;
442-
hh.hash_root().map_err(map_hasher_error)
443-
}
444-
445-
fn hash_unsigned_partial_exit_request_with<H: HashWalker>(
446-
req: &UnsignedPartialExitRequest,
447-
hh: &mut H,
448-
) -> Result<()> {
449-
let index = hh.index();
450-
451-
hash_partial_exits_with(&req.partial_exits, hh)?;
452-
hh.put_uint64(req.share_idx).map_err(map_walker_error)?;
453-
454-
hh.merkleize(index).map_err(map_walker_error)?;
455-
Ok(())
456-
}
457-
458-
fn hash_partial_exits_with<H: HashWalker>(exits: &[ExitBlob], hh: &mut H) -> Result<()> {
459-
let index = hh.index();
460-
let num = exits.len();
461-
462-
for exit_blob in exits {
463-
hash_exit_blob_with(exit_blob, hh)?;
464-
}
465-
466-
hh.merkleize_with_mixin(index, num, SSZ_MAX_EXITS)
467-
.map_err(map_walker_error)?;
468-
Ok(())
469-
}
470-
471-
fn hash_full_exit_auth_blob(blob: &FullExitAuthBlob) -> Result<[u8; 32]> {
472-
let mut hh = Hasher::default();
473-
hash_full_exit_auth_blob_with(blob, &mut hh)?;
474-
hh.hash_root().map_err(map_hasher_error)
475-
}
476-
477-
fn hash_full_exit_auth_blob_with<H: HashWalker>(blob: &FullExitAuthBlob, hh: &mut H) -> Result<()> {
478-
let index = hh.index();
479-
480-
hh.put_bytes(&blob.lock_hash).map_err(map_walker_error)?;
481-
put_bytes_n(hh, &blob.validator_pubkey, SSZ_LEN_PUB_KEY)?;
482-
hh.put_uint64(blob.share_index).map_err(map_walker_error)?;
483-
484-
hh.merkleize(index).map_err(map_walker_error)?;
485-
Ok(())
486-
}
487-
488-
fn hash_signed_voluntary_exit_with<H: HashWalker>(
489-
exit: &SignedVoluntaryExit,
490-
hh: &mut H,
491-
) -> Result<()> {
492-
let index = hh.index();
493-
494-
hash_voluntary_exit_with(&exit.message, hh)?;
495-
let sig_bytes = from_0x(&exit.signature, SSZ_LEN_BLS_SIG)?;
496-
put_bytes_n(hh, &sig_bytes, SSZ_LEN_BLS_SIG)?;
497-
498-
hh.merkleize(index).map_err(map_walker_error)?;
499-
Ok(())
500-
}
501-
502-
fn hash_voluntary_exit_with<H: HashWalker>(
503-
message: &Phase0SignedVoluntaryExitMessage,
504-
hh: &mut H,
505-
) -> Result<()> {
506-
let index = hh.index();
507-
508-
let epoch = message.epoch.parse::<u64>().map_err(Error::EpochParse)?;
509-
let validator_index = message
510-
.validator_index
511-
.parse::<u64>()
512-
.map_err(Error::EpochParse)?;
513-
514-
hh.put_uint64(epoch).map_err(map_walker_error)?;
515-
hh.put_uint64(validator_index).map_err(map_walker_error)?;
516-
517-
hh.merkleize(index).map_err(map_walker_error)?;
518-
Ok(())
519-
}
520-
521465
#[cfg(test)]
522466
mod tests {
523467
use super::*;

0 commit comments

Comments
 (0)