eth-valkyoth-protocol 0.25.2

Fork-aware no_std Ethereum protocol validation state.
Documentation
use eth_valkyoth_primitives::{Address, ChainId, Nonce};

use super::{
    AuthorizationValidationSummary, SetCodeAuthorityAccount, SetCodeAuthorityCode,
    SetCodeAuthorityStateView, SetCodeAuthorization, SetCodeAuthorizationAuthorityView,
    SetCodeTransactionValidityError, UnvalidatedSetCodeTransaction,
};

pub(super) fn validate_authorizations<A, S>(
    transaction: UnvalidatedSetCodeTransaction<'_>,
    authorities: &A,
    accounts: &S,
) -> AuthorizationValidationSummary
where
    A: SetCodeAuthorizationAuthorityView + ?Sized,
    S: SetCodeAuthorityStateView + ?Sized,
{
    let mut summary = AuthorizationValidationSummary::default();
    for (authorization_index, authorization) in
        transaction.authorization_list.authorizations().enumerate()
    {
        let Ok(authorization) = authorization else {
            summary.skipped = summary.skipped.saturating_add(1);
            continue;
        };
        if validate_authorization(
            transaction,
            authorization_index,
            authorization,
            authorities,
            accounts,
        )
        .is_ok()
        {
            summary.applied = summary.applied.saturating_add(1);
        } else {
            summary.skipped = summary.skipped.saturating_add(1);
        }
    }
    summary
}

fn validate_authorization<A, S>(
    transaction: UnvalidatedSetCodeTransaction<'_>,
    authorization_index: usize,
    authorization: SetCodeAuthorization,
    authorities: &A,
    accounts: &S,
) -> Result<(), SetCodeTransactionValidityError>
where
    A: SetCodeAuthorizationAuthorityView + ?Sized,
    S: SetCodeAuthorityStateView + ?Sized,
{
    if !authorization_chain_permits(authorization, transaction.chain_id) {
        return Err(SetCodeTransactionValidityError::WrongAuthorizationChain {
            authorization_index,
        });
    }
    if authorization.nonce.get() == u64::MAX {
        return Err(SetCodeTransactionValidityError::AuthorizationNonceTooHigh {
            authorization_index,
        });
    }
    let authority = authorities
        .authority_for(authorization_index, authorization)
        .ok_or(
            SetCodeTransactionValidityError::MissingAuthorizationAuthority {
                authorization_index,
            },
        )?;
    let account = accounts.authority_account(authority).ok_or(
        SetCodeTransactionValidityError::MissingAuthorityState {
            authorization_index,
        },
    )?;
    if !authority_code_can_apply(account.code) {
        return Err(SetCodeTransactionValidityError::InvalidAuthorityCode {
            authorization_index,
        });
    }
    let prior = count_prior_applications(
        transaction,
        authorization_index,
        authorities,
        authority,
        account,
    )?;
    if checked_authority_nonce(account.nonce, prior, authorization_index)? != authorization.nonce {
        return Err(SetCodeTransactionValidityError::AuthorityNonceMismatch {
            authorization_index,
        });
    }
    Ok(())
}

fn count_prior_applications<A>(
    transaction: UnvalidatedSetCodeTransaction<'_>,
    authorization_index: usize,
    authorities: &A,
    authority: Address,
    account: SetCodeAuthorityAccount,
) -> Result<u64, SetCodeTransactionValidityError>
where
    A: SetCodeAuthorizationAuthorityView + ?Sized,
{
    let mut applied = 0_u64;
    for (earlier_index, earlier) in transaction
        .authorization_list
        .authorizations()
        .take(authorization_index)
        .enumerate()
    {
        let Ok(earlier) = earlier else {
            continue;
        };
        if !prior_authorization_can_apply(
            transaction.chain_id,
            earlier_index,
            earlier,
            authorities,
            authority,
        ) {
            continue;
        }
        if checked_authority_nonce(account.nonce, applied, authorization_index)? == earlier.nonce {
            applied = applied.checked_add(1).ok_or(
                SetCodeTransactionValidityError::AuthorizationNonceTooHigh {
                    authorization_index,
                },
            )?;
        }
    }
    Ok(applied)
}

fn prior_authorization_can_apply<A>(
    chain_id: ChainId,
    authorization_index: usize,
    authorization: SetCodeAuthorization,
    authorities: &A,
    authority: Address,
) -> bool
where
    A: SetCodeAuthorizationAuthorityView + ?Sized,
{
    authorization.nonce.get() != u64::MAX
        && authorization_chain_permits(authorization, chain_id)
        && authorities.authority_for(authorization_index, authorization) == Some(authority)
}

fn checked_authority_nonce(
    base: Nonce,
    offset: u64,
    authorization_index: usize,
) -> Result<Nonce, SetCodeTransactionValidityError> {
    base.get().checked_add(offset).map(Nonce::new).ok_or(
        SetCodeTransactionValidityError::AuthorizationNonceTooHigh {
            authorization_index,
        },
    )
}

fn authority_code_can_apply(code: SetCodeAuthorityCode) -> bool {
    matches!(
        code,
        SetCodeAuthorityCode::Empty | SetCodeAuthorityCode::Delegation { .. }
    )
}

fn authorization_chain_permits(authorization: SetCodeAuthorization, chain_id: ChainId) -> bool {
    if authorization.chain_id.is_universal() {
        return true;
    }
    let bytes = authorization.chain_id.to_be_bytes();
    let Some(prefix) = bytes.get(..24) else {
        return false;
    };
    let Some(suffix) = bytes.get(24..) else {
        return false;
    };
    prefix.iter().all(|byte| *byte == 0) && suffix == chain_id.get().to_be_bytes()
}