eth-valkyoth-primitives 0.9.1

Core no_std Ethereum protocol primitives.
Documentation
use super::*;
use eth_valkyoth_codec::{rlp_integer_payload_to_u64, rlp_integer_payload_to_u256_bytes};

#[test]
fn chain_id_round_trips() {
    assert_eq!(u64::from(ChainId::from(1)), 1);
}

#[test]
fn block_number_round_trips() {
    assert_eq!(u64::from(BlockNumber::from(2)), 2);
}

#[test]
fn gas_round_trips() {
    assert_eq!(u64::from(Gas::from(21_000)), 21_000);
}

#[test]
fn nonce_round_trips() {
    assert_eq!(u64::from(Nonce::from(7)), 7);
}

#[test]
fn unix_timestamp_round_trips() {
    assert_eq!(u64::from(UnixTimestamp::from(1_700_000_000)), 1_700_000_000);
}

#[test]
fn id_types_parse_canonical_rlp_integer_payloads() {
    assert_eq!(
        ChainId::try_from_canonical_be_slice(&[]),
        Ok(ChainId::new(0))
    );
    assert_eq!(
        ChainId::try_from_canonical_be_slice(&[0x04, 0x00]),
        Ok(ChainId::new(1024))
    );
    assert_eq!(
        Gas::try_from_canonical_be_slice(&[0x00, 0x01]),
        Err(PrimitiveError::NonCanonicalInteger)
    );
    assert_eq!(
        Nonce::try_from_canonical_be_slice(&[1_u8; 9]),
        Err(PrimitiveError::IntegerTooLarge)
    );
}

#[test]
fn id_types_delegate_canonical_payload_parsing_to_codec() {
    let cases: &[&[u8]] = &[&[], &[0x01], &[0x7f], &[0x80], &[0x04, 0x00], &[1_u8; 8]];

    for bytes in cases {
        assert_eq!(
            ChainId::try_from_canonical_be_slice(bytes).map(ChainId::get),
            rlp_integer_payload_to_u64(bytes).map_err(map_rlp_integer_error)
        );
        assert_eq!(
            BlockNumber::try_from_canonical_be_slice(bytes).map(BlockNumber::get),
            rlp_integer_payload_to_u64(bytes).map_err(map_rlp_integer_error)
        );
        assert_eq!(
            Gas::try_from_canonical_be_slice(bytes).map(Gas::get),
            rlp_integer_payload_to_u64(bytes).map_err(map_rlp_integer_error)
        );
        assert_eq!(
            Nonce::try_from_canonical_be_slice(bytes).map(Nonce::get),
            rlp_integer_payload_to_u64(bytes).map_err(map_rlp_integer_error)
        );
        assert_eq!(
            UnixTimestamp::try_from_canonical_be_slice(bytes).map(UnixTimestamp::get),
            rlp_integer_payload_to_u64(bytes).map_err(map_rlp_integer_error)
        );
    }
}

#[test]
fn id_types_map_codec_payload_errors_to_primitive_errors() {
    let noncanonical: &[u8] = &[0x00, 0x01];
    let too_large: &[u8] = &[1_u8; 9];

    assert_eq!(
        rlp_integer_payload_to_u64(noncanonical),
        Err(eth_valkyoth_codec::DecodeError::Malformed)
    );
    assert_eq!(
        ChainId::try_from_canonical_be_slice(noncanonical),
        Err(PrimitiveError::NonCanonicalInteger)
    );

    assert_eq!(
        rlp_integer_payload_to_u64(too_large),
        Err(eth_valkyoth_codec::DecodeError::LengthOverflow)
    );
    assert_eq!(
        ChainId::try_from_canonical_be_slice(too_large),
        Err(PrimitiveError::IntegerTooLarge)
    );
}

#[test]
fn address_round_trips() {
    let bytes = [7_u8; 20];
    assert_eq!(<[u8; 20]>::from(Address::from(bytes)), bytes);
}

#[test]
fn address_constant_time_equality_result_matches_equality() {
    let left = Address::from_bytes([1_u8; 20]);
    let same = Address::from_bytes([1_u8; 20]);
    let different = Address::from_bytes([2_u8; 20]);

    assert!(bool::from(left.ct_eq(&same)));
    assert!(!bool::from(left.ct_eq(&different)));
    assert!(left == same);
    assert!(left != different);
}

#[test]
fn b256_constant_time_equality_result_matches_equality() {
    let left = B256::from_bytes([1_u8; 32]);
    let same = B256::from_bytes([1_u8; 32]);
    let different = B256::from_bytes([2_u8; 32]);
    assert!(bool::from(left.ct_eq(&same)));
    assert!(!bool::from(left.ct_eq(&different)));
    assert!(left == same);
    assert!(left != different);
}

#[test]
fn b256_constant_time_choices_compose_without_short_circuit() {
    let left = B256::from_bytes([1_u8; 32]);
    let same = B256::from_bytes([1_u8; 32]);
    let different = B256::from_bytes([2_u8; 32]);
    let composed = left.ct_eq(&same) & same.ct_eq(&different);

    assert!(!bool::from(composed));
}

#[test]
fn b256_round_trips() {
    let bytes = [3_u8; 32];
    assert_eq!(<[u8; 32]>::from(B256::from(bytes)), bytes);
}

#[test]
fn wei_round_trips() {
    let bytes = [9_u8; 32];
    assert_eq!(<[u8; 32]>::from(Wei::from(bytes)), bytes);
}

#[test]
fn wei_parses_canonical_rlp_integer_payloads() {
    assert_eq!(Wei::try_from_canonical_be_slice(&[]), Ok(Wei::ZERO));
    assert_eq!(
        Wei::try_from_canonical_be_slice(&[0x04, 0x00]).map(Wei::to_be_bytes),
        Ok(Wei::from_u128(1024).to_be_bytes())
    );
    assert_eq!(
        Wei::try_from_canonical_be_slice(&[0x00]),
        Err(PrimitiveError::NonCanonicalInteger)
    );
    assert_eq!(
        Wei::try_from_canonical_be_slice(&[1_u8; 33]),
        Err(PrimitiveError::IntegerTooLarge)
    );
}

#[test]
fn wei_parses_canonical_rlp_integer_at_max_width() {
    let max_bytes = [0xff_u8; 32];

    assert_eq!(
        Wei::try_from_canonical_be_slice(&max_bytes).map(Wei::to_be_bytes),
        Ok(max_bytes)
    );
}

#[test]
fn wei_delegates_canonical_payload_parsing_to_codec() {
    let cases: &[&[u8]] = &[
        &[],
        &[0x01],
        &[0x7f],
        &[0x80],
        &[0x04, 0x00],
        &[0xff_u8; 32],
    ];

    for bytes in cases {
        assert_eq!(
            Wei::try_from_canonical_be_slice(bytes).map(Wei::to_be_bytes),
            rlp_integer_payload_to_u256_bytes(bytes).map_err(map_rlp_integer_error)
        );
    }

    assert_eq!(
        Wei::try_from_canonical_be_slice(&[0x00, 0x01]),
        Err(PrimitiveError::NonCanonicalInteger)
    );
    assert_eq!(
        Wei::try_from_canonical_be_slice(&[1_u8; 33]),
        Err(PrimitiveError::IntegerTooLarge)
    );
}

#[test]
fn wei_constant_time_equality_result_matches_equality() {
    let left = Wei::from_be_bytes([1_u8; 32]);
    let same = Wei::from_be_bytes([1_u8; 32]);
    let different = Wei::from_be_bytes([2_u8; 32]);

    assert!(bool::from(left.ct_eq(&same)));
    assert!(!bool::from(left.ct_eq(&different)));
    assert!(left == same);
    assert!(left != different);
}

#[test]
fn wei_from_u128_places_bytes_at_low_end() {
    let wei = Wei::from_u128(1);
    let expected = [
        0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8,
        0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8,
        0_u8, 1_u8,
    ];
    assert_eq!(wei.to_be_bytes(), expected);
}

#[test]
fn transaction_type_accepts_eip_2718_range() {
    let tx_type = TransactionType::try_new(TransactionType::MAX_TYPED);
    assert_eq!(tx_type.map(TransactionType::get), Ok(0x7f));
}

#[test]
fn transaction_type_rejects_reserved_range() {
    assert_eq!(
        TransactionType::try_new(0x80),
        Err(PrimitiveError::TransactionTypeTooLarge)
    );
}

#[test]
fn typed_transaction_type_rejects_legacy_zero() {
    assert_eq!(
        TransactionType::try_new_typed(0),
        Err(PrimitiveError::ReservedLegacyType)
    );
    assert_eq!(TransactionType::try_new_typed(1).map(u8::from), Ok(1));
    assert_eq!(
        TransactionType::try_from(0),
        Err(PrimitiveError::ReservedLegacyType)
    );
}

#[test]
fn transaction_type_round_trips() {
    assert_eq!(TransactionType::try_new(2).map(u8::from), Ok(2));
    assert_eq!(TransactionType::try_new_with_legacy(0).map(u8::from), Ok(0));
}