eth-state-diff 0.2.3

Fork-aware, domain-specific delta encoding for Ethereum consensus-layer state.
Documentation
//! Delta encoding for the Eth1 data vote list.
//!
//! Eth1 data votes accumulate during an Eth1 voting period and are reset when
//! the voting period changes. This module represents those transitions using
//! either an append-only update or a complete replacement.
//!
//! The delta operates directly on the serialized SSZ representation and does
//! not deserialize individual Eth1 data values.

use crate::types::{ArchivedEth1DataVotesDiff, Eth1DataVotesDiff};

/// Computes a delta between two serialized Eth1 data vote lists.
///
/// If the target preserves the complete serialized base list, only the bytes
/// appended to the base are stored in [`Eth1DataVotesDiff::Append`].
/// Otherwise, the complete target list is stored in
/// [`Eth1DataVotesDiff::ResetAndAppend`].
///
/// This covers both accumulation of votes during an Eth1 voting period and
/// replacement of the vote list when the voting period changes.
///
/// This function operates directly on serialized SSZ bytes and does not
/// deserialize individual votes.
///
/// # Arguments
///
/// * `base` - Serialized SSZ representation of the current vote list.
/// * `target` - Serialized SSZ representation of the target vote list.
///
/// # Returns
///
/// A delta representing the transition from `base` to `target`.
///
/// # Complexity
///
/// O(n), where *n* is the length of `base`, for the prefix comparison.
/// Additional space is proportional to the selected delta payload.
///
/// # Example
///
/// ```
/// use eth_state_diff::eth1_data_votes::diff_eth1_votes;
/// use eth_state_diff::types::Eth1DataVotesDiff;
///
/// let base = b"AAAA";
/// let target = b"AAAABBBB";
///
/// let delta = diff_eth1_votes(base, target);
///
/// assert_eq!(delta, Eth1DataVotesDiff::Append(b"BBBB".to_vec()));
/// ```
pub fn diff_eth1_votes(base: &[u8], target: &[u8]) -> Eth1DataVotesDiff {
    if target.starts_with(base) {
        let appended_votes = &target[base.len()..];
        Eth1DataVotesDiff::Append(appended_votes.to_vec())
    } else {
        Eth1DataVotesDiff::ResetAndAppend(target.to_vec())
    }
}

/// Applies an Eth1 data vote delta to a serialized vote list in place.
///
/// [`Eth1DataVotesDiff::Append`] preserves the existing bytes and appends the
/// delta payload.
///
/// [`Eth1DataVotesDiff::ResetAndAppend`] clears the existing vote list before
/// writing the replacement payload.
///
/// The delta is assumed to have been produced for the current base state.
/// This function does not validate that an append delta's base bytes match
/// the state being modified.
///
/// # Arguments
///
/// * `base` - Serialized SSZ vote list to modify.
/// * `delta` - Archived delta to apply.
///
/// # Complexity
///
/// - [`Eth1DataVotesDiff::Append`]: O(k), where *k* is the number of appended
///   bytes.
/// - [`Eth1DataVotesDiff::ResetAndAppend`]: O(k), where *k* is the size of the
///   replacement payload.
///
/// # Example
///
/// ```
/// use eth_state_diff::eth1_data_votes::{apply_eth1_votes, diff_eth1_votes};
/// use eth_state_diff::types::Eth1DataVotesDiff;
///
/// let mut base = b"AAAA".to_vec();
/// let delta = diff_eth1_votes(&base, b"AAAABBBB");
///
/// // The delta is generated locally, so its contents can be applied directly.
/// match delta {
///     Eth1DataVotesDiff::Append(appended_votes) => {
///         base.extend_from_slice(&appended_votes);
///     }
///     Eth1DataVotesDiff::ResetAndAppend(replacement) => {
///         base.clear();
///         base.extend_from_slice(&replacement);
///     }
/// }
///
/// assert_eq!(base, b"AAAABBBB");
/// ```
///
/// [`Eth1DataVotesDiff`]: crate::types::Eth1DataVotesDiff
pub fn apply_eth1_votes(base: &mut Vec<u8>, delta: &ArchivedEth1DataVotesDiff) {
    match delta {
        ArchivedEth1DataVotesDiff::Append(appended_votes) => {
            base.extend_from_slice(appended_votes.as_slice());
        }
        ArchivedEth1DataVotesDiff::ResetAndAppend(replacement) => {
            base.clear();
            base.extend_from_slice(replacement.as_slice());
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::ArchivedEth1DataVotesDiff;

    /// Helper to serialize, archive, and apply a delta.
    fn assert_roundtrip(base: &[u8], target: &[u8]) {
        let delta = diff_eth1_votes(base, target);

        let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&delta).expect("test setup: serialize");
        let archived = rkyv::access::<ArchivedEth1DataVotesDiff, rkyv::rancor::Error>(&bytes)
            .expect("test setup: failed to access archived eth1 votes delta");

        let mut reconstructed = base.to_vec();
        apply_eth1_votes(&mut reconstructed, archived);

        assert_eq!(reconstructed, target);
    }

    #[test]
    fn test_diff_apply_pure_append() {
        let base = b"AAAA";
        let target = b"AAAABBBB";

        let delta = diff_eth1_votes(base, target);
        assert!(matches!(delta, Eth1DataVotesDiff::Append(ref v) if v == b"BBBB"));

        assert_roundtrip(base, target);
    }

    #[test]
    fn test_diff_apply_identical_lists() {
        let base = b"AAAA";
        let target = b"AAAA";

        let delta = diff_eth1_votes(base, target);
        // Starts with is true, slice is empty
        assert!(matches!(delta, Eth1DataVotesDiff::Append(ref v) if v.is_empty()));

        assert_roundtrip(base, target);
    }

    #[test]
    fn test_diff_apply_empty_base() {
        let base = b"";
        let target = b"BBBB";

        let delta = diff_eth1_votes(base, target);
        // Empty string is a prefix of everything
        assert!(matches!(delta, Eth1DataVotesDiff::Append(ref v) if v == b"BBBB"));

        assert_roundtrip(base, target);
    }

    #[test]
    fn test_diff_apply_complete_replacement() {
        let base = b"AAAA";
        let target = b"CCCC";

        let delta = diff_eth1_votes(base, target);
        assert!(matches!(delta, Eth1DataVotesDiff::ResetAndAppend(ref v) if v == b"CCCC"));

        assert_roundtrip(base, target);
    }

    #[test]
    fn test_diff_apply_target_shorter_than_base() {
        let base = b"AAAABBBB";
        let target = b"AA";

        let delta = diff_eth1_votes(base, target);
        assert!(matches!(delta, Eth1DataVotesDiff::ResetAndAppend(ref v) if v == b"AA"));

        assert_roundtrip(base, target);
    }

    #[test]
    fn test_diff_apply_historical_vote_modified() {
        // CRITICAL EDGE CASE: The prefix matches up to a point, but an older
        // vote in the middle of the base was modified. `starts_with` returns false,
        // forcing a full replacement to prevent corruption.
        let base = b"AAAA-XXXX";
        let target = b"AAAA-YYYY";

        let delta = diff_eth1_votes(base, target);
        assert!(matches!(delta, Eth1DataVotesDiff::ResetAndAppend(ref v) if v == b"AAAA-YYYY"));

        assert_roundtrip(base, target);
    }
}