Skip to content

Commit

Permalink
feat: add block by number and remove epoch accumulators
Browse files Browse the repository at this point in the history
  • Loading branch information
KolbyML committed Sep 16, 2024
1 parent d2194a7 commit a2147d3
Show file tree
Hide file tree
Showing 34 changed files with 736 additions and 663 deletions.
4 changes: 1 addition & 3 deletions ethportal-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ pub use types::{
content_key::{
beacon::{BeaconContentKey, LightClientBootstrapKey, LightClientUpdatesByRangeKey},
error::ContentKeyError,
history::{
BlockBodyKey, BlockHeaderKey, BlockReceiptsKey, EpochAccumulatorKey, HistoryContentKey,
},
history::{BlockBodyKey, BlockReceiptsKey, HistoryContentKey},
overlay::{IdentityContentKey, OverlayContentKey},
state::StateContentKey,
},
Expand Down
173 changes: 82 additions & 91 deletions ethportal-api/src/types/content_key/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloy_primitives::B256;
use rand::{seq::SliceRandom, RngCore};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::{Digest as Sha2Digest, Sha256};
use ssz::{Decode, DecodeError};
use ssz::{Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use std::{fmt, hash::Hash};

Expand All @@ -14,36 +14,41 @@ use crate::{

// Prefixes for the different types of history content keys:
// https://github.com/ethereum/portal-network-specs/blob/638aca50c913a749d0d762264d9a4ac72f1a9966/history-network.md
pub const HISTORY_BLOCK_HEADER_KEY_PREFIX: u8 = 0x00;
pub const HISTORY_BLOCK_HEADER_BY_HASH_KEY_PREFIX: u8 = 0x00;
pub const HISTORY_BLOCK_BODY_KEY_PREFIX: u8 = 0x01;
pub const HISTORY_BLOCK_RECEIPTS_KEY_PREFIX: u8 = 0x02;
pub const HISTORY_BLOCK_EPOCH_ACCUMULATOR_KEY_PREFIX: u8 = 0x03;
pub const HISTORY_BLOCK_HEADER_BY_NUMBER_KEY_PREFIX: u8 = 0x03;

/// A content key in the history overlay network.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HistoryContentKey {
/// A block header with accumulator proof.
BlockHeaderWithProof(BlockHeaderKey),
BlockHeaderByHashWithProof(BlockHeaderByHashKey),
/// A block header with accumulator proof.
BlockHeaderByNumberWithProof(BlockHeaderByNumberKey),
/// A block body.
BlockBody(BlockBodyKey),
/// The transaction receipts for a block.
BlockReceipts(BlockReceiptsKey),
/// An epoch header accumulator.
EpochAccumulator(EpochAccumulatorKey),
}

impl HistoryContentKey {
pub fn random() -> anyhow::Result<Self> {
let mut random_bytes: Vec<u8> = vec![0u8; 32];
rand::thread_rng().fill_bytes(&mut random_bytes[..]);
let random_prefix = [
HISTORY_BLOCK_HEADER_KEY_PREFIX,
HISTORY_BLOCK_HEADER_BY_HASH_KEY_PREFIX,
HISTORY_BLOCK_BODY_KEY_PREFIX,
HISTORY_BLOCK_RECEIPTS_KEY_PREFIX,
HISTORY_BLOCK_EPOCH_ACCUMULATOR_KEY_PREFIX,
HISTORY_BLOCK_HEADER_BY_NUMBER_KEY_PREFIX,
]
.choose(&mut rand::thread_rng())
.ok_or_else(|| anyhow::Error::msg("Failed to choose random prefix"))?;
let mut random_bytes: Vec<u8> =
if *random_prefix == HISTORY_BLOCK_HEADER_BY_NUMBER_KEY_PREFIX {
vec![0u8; 8]
} else {
vec![0u8; 32]
};
rand::thread_rng().fill_bytes(&mut random_bytes[..]);
random_bytes.insert(0, *random_prefix);
let random_bytes: RawContentKey = random_bytes.into();
Self::try_from(random_bytes).map_err(anyhow::Error::msg)
Expand Down Expand Up @@ -75,21 +80,34 @@ impl<'de> Deserialize<'de> for HistoryContentKey {
}
}

/// A key for a block header.
/// A key for a block header by hash.
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, Default)]
pub struct BlockHeaderKey {
pub struct BlockHeaderByHashKey {
/// Hash of the block.
pub block_hash: [u8; 32],
}

impl From<B256> for BlockHeaderKey {
impl From<B256> for BlockHeaderByHashKey {
fn from(block_hash: B256) -> Self {
Self {
block_hash: block_hash.0,
}
}
}

/// A key for a block header by number.
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, Default)]
pub struct BlockHeaderByNumberKey {
/// Number of the block.
pub block_number: u64,
}

impl From<u64> for BlockHeaderByNumberKey {
fn from(block_number: u64) -> Self {
Self { block_number }
}
}

/// A key for a block body.
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq)]
pub struct BlockBodyKey {
Expand All @@ -112,12 +130,6 @@ pub struct BlockReceiptsKey {
pub block_hash: [u8; 32],
}

/// A key for an epoch header accumulator.
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq)]
pub struct EpochAccumulatorKey {
pub epoch_hash: B256,
}

impl TryFrom<RawContentKey> for HistoryContentKey {
type Error = ContentKeyError;

Expand All @@ -129,18 +141,20 @@ impl TryFrom<RawContentKey> for HistoryContentKey {
});
};
match selector {
HISTORY_BLOCK_HEADER_KEY_PREFIX => BlockHeaderKey::from_ssz_bytes(key)
.map(Self::BlockHeaderWithProof)
HISTORY_BLOCK_HEADER_BY_HASH_KEY_PREFIX => BlockHeaderByHashKey::from_ssz_bytes(key)
.map(Self::BlockHeaderByHashWithProof)
.map_err(|e| ContentKeyError::from_decode_error(e, value)),
HISTORY_BLOCK_BODY_KEY_PREFIX => BlockBodyKey::from_ssz_bytes(key)
.map(Self::BlockBody)
.map_err(|e| ContentKeyError::from_decode_error(e, value)),
HISTORY_BLOCK_RECEIPTS_KEY_PREFIX => BlockReceiptsKey::from_ssz_bytes(key)
.map(Self::BlockReceipts)
.map_err(|e| ContentKeyError::from_decode_error(e, value)),
HISTORY_BLOCK_EPOCH_ACCUMULATOR_KEY_PREFIX => EpochAccumulatorKey::from_ssz_bytes(key)
.map(Self::EpochAccumulator)
.map_err(|e| ContentKeyError::from_decode_error(e, value)),
HISTORY_BLOCK_HEADER_BY_NUMBER_KEY_PREFIX => {
BlockHeaderByNumberKey::from_ssz_bytes(key)
.map(Self::BlockHeaderByNumberWithProof)
.map_err(|e| ContentKeyError::from_decode_error(e, value))
}
_ => Err(ContentKeyError::from_decode_error(
DecodeError::UnionSelectorInvalid(selector),
value,
Expand All @@ -152,8 +166,8 @@ impl TryFrom<RawContentKey> for HistoryContentKey {
impl fmt::Display for HistoryContentKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::BlockHeaderWithProof(header) => format!(
"BlockHeaderWithProof {{ block_hash: {} }}",
Self::BlockHeaderByHashWithProof(header) => format!(
"BlockHeaderByHashWithProof {{ block_hash: {} }}",
hex_encode_compact(header.block_hash)
),
Self::BlockBody(body) => format!(
Expand All @@ -166,10 +180,10 @@ impl fmt::Display for HistoryContentKey {
hex_encode_compact(receipts.block_hash)
)
}
Self::EpochAccumulator(acc) => {
Self::BlockHeaderByNumberWithProof(header) => {
format!(
"EpochAccumulator {{ epoch_hash: {} }}",
hex_encode_compact(acc.epoch_hash.0)
"BlockHeaderByNumberWithProof {{ block_number: {} }}",
header.block_number
)
}
};
Expand All @@ -189,8 +203,8 @@ impl OverlayContentKey for HistoryContentKey {
let mut bytes: Vec<u8> = Vec::new();

match self {
HistoryContentKey::BlockHeaderWithProof(k) => {
bytes.push(HISTORY_BLOCK_HEADER_KEY_PREFIX);
HistoryContentKey::BlockHeaderByHashWithProof(k) => {
bytes.push(HISTORY_BLOCK_HEADER_BY_HASH_KEY_PREFIX);
bytes.extend_from_slice(&k.block_hash);
}
HistoryContentKey::BlockBody(k) => {
Expand All @@ -201,9 +215,9 @@ impl OverlayContentKey for HistoryContentKey {
bytes.push(HISTORY_BLOCK_RECEIPTS_KEY_PREFIX);
bytes.extend_from_slice(&k.block_hash);
}
HistoryContentKey::EpochAccumulator(k) => {
bytes.push(HISTORY_BLOCK_EPOCH_ACCUMULATOR_KEY_PREFIX);
bytes.extend_from_slice(&k.epoch_hash.0);
HistoryContentKey::BlockHeaderByNumberWithProof(k) => {
bytes.push(HISTORY_BLOCK_HEADER_BY_NUMBER_KEY_PREFIX);
bytes.extend_from_slice(&k.block_number.as_ssz_bytes());
}
}

Expand All @@ -224,7 +238,7 @@ mod test {
];

#[test]
fn block_header() {
fn block_header_by_hash() {
const KEY_STR: &str =
"0x00d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d";
let expected_content_key = hex_decode(KEY_STR).unwrap();
Expand All @@ -234,17 +248,43 @@ mod test {
0x6e, 0x38, 0x95, 0xfe,
];

let header = BlockHeaderKey {
let header = BlockHeaderByHashKey {
block_hash: BLOCK_HASH,
};

let key = HistoryContentKey::BlockHeaderWithProof(header);
let key = HistoryContentKey::BlockHeaderByHashWithProof(header);

assert_eq!(key.to_bytes(), expected_content_key);
assert_eq!(key.content_id(), expected_content_id);
assert_eq!(
key.to_string(),
"BlockHeaderWithProof { block_hash: 0xd1c3..621d }"
"BlockHeaderByHashWithProof { block_hash: 0xd1c3..621d }"
);
assert_eq!(key.to_hex(), KEY_STR);
}

#[test]
fn block_header_by_number() {
const BLOCK_NUMBER: u64 = 12345678;
const KEY_STR: &str = "0x034e61bc0000000000";
let expected_content_key = hex_decode(KEY_STR).unwrap();
let expected_content_id: [u8; 32] =
hex_decode("0x2113990747a85ab39785d21342fa5db1f68acc0011605c0c73f68fc331643dcf")
.unwrap()
.try_into()
.unwrap();

let header = BlockHeaderByNumberKey {
block_number: BLOCK_NUMBER,
};

let key = HistoryContentKey::BlockHeaderByNumberWithProof(header);

assert_eq!(key.to_bytes(), expected_content_key);
assert_eq!(key.content_id(), expected_content_id);
assert_eq!(
key.to_string(),
"BlockHeaderByNumberWithProof { block_number: 12345678 }"
);
assert_eq!(key.to_hex(), KEY_STR);
}
Expand Down Expand Up @@ -298,43 +338,14 @@ mod test {
assert_eq!(key.to_hex(), KEY_STR);
}

// test values sourced from: https://github.com/ethereum/portal-network-specs/blob/master/content-keys-test-vectors.md
#[test]
fn epoch_accumulator_key() {
let epoch_hash =
hex_decode("0xe242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491")
.unwrap();
const KEY_STR: &str =
"0x03e242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491";
let expected_content_key = hex_decode(KEY_STR).unwrap();
let expected_content_id =
hex_decode("0x9fb2175e76c6989e0fdac3ee10c40d2a81eb176af32e1c16193e3904fe56896e")
.unwrap();

let key = HistoryContentKey::EpochAccumulator(EpochAccumulatorKey {
epoch_hash: B256::from_slice(&epoch_hash),
});

// round trip
let decoded = HistoryContentKey::try_from(key.to_bytes()).unwrap();
assert_eq!(decoded, key);

assert_eq!(key.to_bytes(), expected_content_key);
assert_eq!(key.content_id(), expected_content_id.as_ref() as &[u8]);
assert_eq!(
key.to_string(),
"EpochAccumulator { epoch_hash: 0xe242..c491 }"
);
assert_eq!(key.to_hex(), KEY_STR);
}

#[test]
fn ser_de_block_header() {
let content_key_json =
"\"0x00d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d\"";
let expected_content_key = HistoryContentKey::BlockHeaderWithProof(BlockHeaderKey {
block_hash: BLOCK_HASH,
});
let expected_content_key =
HistoryContentKey::BlockHeaderByHashWithProof(BlockHeaderByHashKey {
block_hash: BLOCK_HASH,
});

let content_key: HistoryContentKey = serde_json::from_str(content_key_json).unwrap();

Expand Down Expand Up @@ -389,24 +400,4 @@ mod test {
content_key_json
);
}

#[test]
fn ser_de_epoch_accumulator() {
let content_key_json =
"\"0x03e242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491\"";
let epoch_hash =
hex_decode("0xe242814b90ed3950e13aac7e56ce116540c71b41d1516605aada26c6c07cc491")
.unwrap();
let expected_content_key = HistoryContentKey::EpochAccumulator(EpochAccumulatorKey {
epoch_hash: B256::from_slice(&epoch_hash),
});

let content_key: HistoryContentKey = serde_json::from_str(content_key_json).unwrap();

assert_eq!(content_key, expected_content_key);
assert_eq!(
serde_json::to_string(&content_key).unwrap(),
content_key_json
);
}
}
27 changes: 4 additions & 23 deletions ethportal-api/src/types/content_value/history.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::{
types::{
cli::HISTORY_NETWORK,
content_value::ContentValue,
execution::{accumulator::EpochAccumulator, header_with_proof::HeaderWithProof},
cli::HISTORY_NETWORK, content_value::ContentValue,
execution::header_with_proof::HeaderWithProof,
},
utils::bytes::hex_encode,
BlockBody, ContentValueError, HistoryContentKey, RawContentValue, Receipts,
Expand All @@ -16,7 +15,6 @@ pub enum HistoryContentValue {
BlockHeaderWithProof(HeaderWithProof),
BlockBody(BlockBody),
Receipts(Receipts),
EpochAccumulator(EpochAccumulator),
}

impl ContentValue for HistoryContentValue {
Expand All @@ -27,13 +25,13 @@ impl ContentValue for HistoryContentValue {
Self::BlockHeaderWithProof(value) => value.as_ssz_bytes().into(),
Self::BlockBody(value) => value.as_ssz_bytes().into(),
Self::Receipts(value) => value.as_ssz_bytes().into(),
Self::EpochAccumulator(value) => value.as_ssz_bytes().into(),
}
}

fn decode(key: &Self::TContentKey, buf: &[u8]) -> Result<Self, ContentValueError> {
match key {
HistoryContentKey::BlockHeaderWithProof(_) => {
HistoryContentKey::BlockHeaderByHashWithProof(_)
| HistoryContentKey::BlockHeaderByNumberWithProof(_) => {
if let Ok(value) = HeaderWithProof::from_ssz_bytes(buf) {
return Ok(Self::BlockHeaderWithProof(value));
}
Expand All @@ -48,11 +46,6 @@ impl ContentValue for HistoryContentValue {
return Ok(Self::Receipts(value));
}
}
HistoryContentKey::EpochAccumulator(_) => {
if let Ok(value) = EpochAccumulator::from_ssz_bytes(buf) {
return Ok(Self::EpochAccumulator(value));
}
}
}

Err(ContentValueError::UnknownContent {
Expand All @@ -71,9 +64,6 @@ mod test {
use crate::{utils::bytes::hex_decode, HistoryContentValue};
use std::fs;

/// Max number of blocks / epoch = 2 ** 13
pub const EPOCH_SIZE: usize = 8192;

#[test]
fn header_with_proof_encode_decode_fluffy() {
let file =
Expand All @@ -95,15 +85,6 @@ mod test {
}
}

#[test]
fn ssz_serde_encode_decode_fluffy_epoch_accumulator() {
// values sourced from: https://github.com/status-im/portal-spec-tests
let epoch_acc_ssz = fs::read("../trin-validation/src/assets/fluffy/epoch_acc.bin").unwrap();
let epoch_acc = EpochAccumulator::from_ssz_bytes(&epoch_acc_ssz).unwrap();
assert_eq!(epoch_acc.len(), EPOCH_SIZE);
assert_eq!(epoch_acc.as_ssz_bytes(), epoch_acc_ssz);
}

#[test]
fn content_value_deserialization_failure_displays_debuggable_data() {
let key = HistoryContentKey::random().unwrap();
Expand Down
Loading

0 comments on commit a2147d3

Please sign in to comment.