Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ethportal-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ rand = "0.8.5"
reth-rpc-types = { tag = "v0.1.0-alpha.10", git = "https://github.com/paradigmxyz/reth.git"}
rlp = "0.5.0"
rlp-derive = "0.1.0"
rs_merkle = "1.4.2"
ruint = { version = "1.9.0", features = ["primitive-types"] }
serde = { version = "1.0.150", features = ["derive"] }
serde_json = "1.0.89"
Expand Down
82 changes: 82 additions & 0 deletions ethportal-api/src/types/consensus/beacon_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use crate::consensus::{
};
use ethereum_types::H256;
use jsonrpsee::core::Serialize;
use rs_merkle::{algorithms::Sha256, MerkleTree};
use serde::Deserialize;
use serde_this_or_that::as_u64;
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use superstruct::superstruct;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;

/// A block of the `BeaconChain`.
Expand Down Expand Up @@ -63,6 +65,34 @@ impl BeaconBlock {
}
}

impl BeaconBlockBellatrix {
pub fn build_body_root_proof(&self) -> Vec<H256> {
let mut leaves: Vec<[u8; 32]> = vec![
self.slot.tree_hash_root().to_fixed_bytes(),
self.proposer_index.tree_hash_root().to_fixed_bytes(),
self.parent_root.tree_hash_root().to_fixed_bytes(),
self.state_root.tree_hash_root().to_fixed_bytes(),
self.body.tree_hash_root().to_fixed_bytes(),
];
// We want to add empty leaves to make the tree a power of 2
while leaves.len() < 8 {
leaves.push([0; 32]);
}

let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
// We want to prove the body root, which is the 5th leaf
let indices_to_prove = vec![4];
let proof = merkle_tree.proof(&indices_to_prove);
let proof_hashes: Vec<H256> = proof
.proof_hashes()
.iter()
.map(|x| H256::from_slice(x))
.collect();

proof_hashes
}
}

/// A `BeaconBlock` and a signature from its proposer.
#[superstruct(
variants(Bellatrix, Capella),
Expand Down Expand Up @@ -137,6 +167,7 @@ mod test {
use ::ssz::Encode;
use rstest::rstest;
use serde_json::Value;
use std::str::FromStr;

#[rstest]
#[case("case_0")]
Expand Down Expand Up @@ -206,4 +237,55 @@ mod test {
SignedBeaconBlock::from_ssz_bytes(&expected, ForkName::Capella).unwrap();
assert_eq!(content.as_ssz_bytes(), expected);
}

#[test]
fn serde_beacon_block_bellatrix() {
let value = std::fs::read_to_string(
"../test_assets/beacon/bellatrix/BeaconBlock/ssz_random/case_0/value.yaml",
)
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: BeaconBlockBellatrix = serde_json::from_value(value.clone()).unwrap();
let serialized = serde_json::to_value(content).unwrap();
assert_eq!(serialized, value);
}

#[test]
fn ssz_beacon_block_bellatrix() {
let value = std::fs::read_to_string(
"../test_assets/beacon/bellatrix/BeaconBlock/ssz_random/case_0/value.yaml",
)
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: BeaconBlockBellatrix = serde_json::from_value(value).unwrap();

let compressed = std::fs::read(
"../test_assets/beacon/bellatrix/BeaconBlock/ssz_random/case_0/serialized.ssz_snappy",
)
.expect("cannot find test asset");
let mut decoder = snap::raw::Decoder::new();
let expected = decoder.decompress_vec(&compressed).unwrap();
BeaconBlock::from_ssz_bytes(&expected, ForkName::Bellatrix).unwrap();
assert_eq!(content.as_ssz_bytes(), expected);
}

#[test]
fn beacon_block_body_root_proof() {
let value = std::fs::read_to_string(
"../test_assets/beacon/bellatrix/BeaconBlock/ssz_random/case_0/value.yaml",
)
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: BeaconBlockBellatrix = serde_json::from_value(value).unwrap();
let expected_proof = [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b",
"0x32b5f53d4b2729823f200eeb36bcf8a78fc1da2d60fef6df87a64a351fce46e7",
]
.map(|x| H256::from_str(x).unwrap());
let proof = content.build_body_root_proof();

assert_eq!(proof.len(), 3);
assert_eq!(proof, expected_proof.to_vec());
}
}
99 changes: 99 additions & 0 deletions ethportal-api/src/types/consensus/beacon_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::consensus::{
use discv5::enr::k256::elliptic_curve::consts::{U1099511627776, U2048, U4, U65536, U8192};
use ethereum_types::H256;
use jsonrpsee::core::Serialize;
use rs_merkle::{algorithms::Sha256, MerkleTree};
use serde::Deserialize;
use serde_this_or_that::as_u64;
use serde_utils;
Expand Down Expand Up @@ -245,13 +246,43 @@ pub struct HistoricalBatch {
pub state_roots: FixedVector<H256, SlotsPerHistoricalRoot>,
}

impl HistoricalBatch {
pub fn build_block_root_proof(&self, block_root_index: u64) -> Vec<H256> {
// Build block hash proof for sel.block_roots
let leaves: Vec<[u8; 32]> = self
.block_roots
.iter()
.map(|root| root.tree_hash_root().to_fixed_bytes())
.collect();

let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
let indices_to_prove = vec![block_root_index as usize];
let proof = merkle_tree.proof(&indices_to_prove);
let mut proof_hashes: Vec<H256> = proof
.proof_hashes()
.iter()
.map(|x| H256::from_slice(x))
.collect();

// To generate proof for block root anchored to the historical batch tree_hash_root, we need
// to add the self.state_root tree_hash_root to the proof_hashes
proof_hashes.push(self.state_roots.tree_hash_root());

// Proof len should always be 14
assert_eq!(proof_hashes.len(), 14);

proof_hashes
}
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test {
use super::*;
use ::ssz::Encode;
use rstest::rstest;
use serde_json::Value;
use std::str::FromStr;

#[rstest]
#[case("case_0")]
Expand Down Expand Up @@ -322,4 +353,72 @@ mod test {
BeaconState::from_ssz_bytes(&expected, ForkName::Capella).unwrap();
assert_eq!(content.as_ssz_bytes(), expected);
}

#[rstest]
#[case("case_0")]
#[case("case_1")]
fn serde_historical_batch(#[case] case: &str) {
let value = std::fs::read_to_string(format!(
"../test_assets/beacon/bellatrix/HistoricalBatch/ssz_random/{case}/value.yaml"
))
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: HistoricalBatch = serde_json::from_value(value.clone()).unwrap();
let serialized = serde_json::to_value(content).unwrap();
assert_eq!(serialized, value);
}

#[rstest]
#[case("case_0")]
#[case("case_1")]
fn ssz_historical_batch(#[case] case: &str) {
let value = std::fs::read_to_string(format!(
"../test_assets/beacon/bellatrix/HistoricalBatch/ssz_random/{case}/value.yaml"
))
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: HistoricalBatch = serde_json::from_value(value).unwrap();

let compressed = std::fs::read(format!(
"../test_assets/beacon/bellatrix/HistoricalBatch/ssz_random/{case}/serialized.ssz_snappy"
))
.expect("cannot find test asset");
let mut decoder = snap::raw::Decoder::new();
let expected = decoder.decompress_vec(&compressed).unwrap();
HistoricalBatch::from_ssz_bytes(&expected).unwrap();
assert_eq!(content.as_ssz_bytes(), expected);
}

#[test]
fn historical_batch_block_root_proof() {
let value = std::fs::read_to_string(
"../test_assets/beacon/bellatrix/HistoricalBatch/ssz_random/case_0/value.yaml",
)
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: HistoricalBatch = serde_json::from_value(value).unwrap();

let expected_proof = [
"0xad500369fa624b7bb451bf1c5119bb6e5e623bab76a0d06948d04a38f35a740d",
"0x222151dcdfbace03dd6f2428ee1a12acffaa1ce03e6966aefd4a48282a776e8e",
"0x28319c59ca450d2fba4b3225eccd994eee98276b7c77e2c3256a3df829767112",
"0x4cd3f3ff2891ef30542d4ae1530c90120cf7718ae936204aa5b0d67ea07957ef",
"0xab543cef2058a48bd9d327dce1ee91ac9acf114f7c0b1762689c79b7d10bb363",
"0x410b45ebb9351cd84dd13a888de4b04d781630df726cec7eb74814f2a00f3c6e",
"0xd213561e8cff461aa94ee50910381ff1182e3fb90a914e17060f3c0c23522911",
"0x84b4db81fe167dd181bcab2ca02008b22f0ab11462f8bd4547e29a6b55bb6a11",
"0x845c6bf5051749dc6b82222c664033ae301fca96c24fd32a63fc3f5adfb1a656",
"0x19a5290c03daf8156f6941cca8feb5b16e843220e2f9b57647a90d76a459d302",
"0x9ca2a640c85ce719174ec29710596f3315aedb4b47044f175b2c39f35bf0d15e",
"0xb7534aed4d180eec8ac7d961e1020028c07d0c83dcdd23f56a7920a08b7393be",
"0xf8a36457194917609bd16697972d616d8f14e71f4fcfd64666e11544bd5f193e",
"0x1d28097093ca99336cb6b3e8c8c34d749a3e43efc9bb6fabc2cfd6ffb1701b08",
]
.map(|x| H256::from_str(x).unwrap());

let proof = content.build_block_root_proof(0);

assert_eq!(proof.len(), 14);
assert_eq!(proof, expected_proof.to_vec());
}
}
78 changes: 78 additions & 0 deletions ethportal-api/src/types/consensus/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
};
use discv5::enr::k256::elliptic_curve::consts::U16;
use ethereum_types::{Address, H256};
use rs_merkle::{algorithms::Sha256, MerkleTree};
use serde::{Deserialize, Serialize};
use serde_this_or_that::as_u64;
use ssz::Decode;
Expand All @@ -18,6 +19,7 @@ use ssz_types::{
BitList, BitVector, FixedVector, VariableList,
};
use superstruct::superstruct;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;

use super::{header::BeaconBlockHeader, pubkey::PubKey, signature::BlsSignature};
Expand Down Expand Up @@ -75,6 +77,46 @@ impl BeaconBlockBody {
}
}

impl BeaconBlockBodyBellatrix {
pub fn build_execution_payload_proof(&self) -> Vec<H256> {
let mut leaves: Vec<[u8; 32]> = vec![
self.randao_reveal.tree_hash_root().to_fixed_bytes(),
self.eth1_data.tree_hash_root().to_fixed_bytes(),
self.graffiti.tree_hash_root().to_fixed_bytes(),
self.proposer_slashings.tree_hash_root().to_fixed_bytes(),
self.attester_slashings.tree_hash_root().to_fixed_bytes(),
self.attestations.tree_hash_root().to_fixed_bytes(),
self.deposits.tree_hash_root().to_fixed_bytes(),
self.voluntary_exits.tree_hash_root().to_fixed_bytes(),
self.sync_aggregate.tree_hash_root().to_fixed_bytes(),
self.execution_payload.tree_hash_root().to_fixed_bytes(),
];
// We want to add empty leaves to make the tree a power of 2
while leaves.len() < 16 {
leaves.push([0; 32]);
}

let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);

// We want to prove the 10th leaf
let indices_to_prove = vec![9];
let proof = merkle_tree.proof(&indices_to_prove);
let proof_hashes: Vec<H256> = proof
.proof_hashes()
.iter()
.map(|x| H256::from_slice(x))
.collect();

proof_hashes
}

pub fn build_execution_block_hash_proof(&self) -> Vec<H256> {
let mut block_hash_proof = self.execution_payload.build_block_hash_proof();
block_hash_proof.extend(self.build_execution_payload_proof());
block_hash_proof
}
}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Decode, Encode, TreeHash)]
pub struct SyncAggregate {
pub sync_committee_bits: BitVector<typenum::U512>,
Expand Down Expand Up @@ -186,6 +228,7 @@ mod test {
use ::ssz::Encode;
use rstest::rstest;
use serde_json::Value;
use std::str::FromStr;

/// Test vectors sourced from:
/// https://github.com/ethereum/consensus-spec-tests/commit/c6e69469a75392b35169bc6234d4d3e6c4e288da
Expand Down Expand Up @@ -229,4 +272,39 @@ mod test {
BeaconBlockBody::from_ssz_bytes(&expected, ForkName::Bellatrix).unwrap();
assert_eq!(body.as_ssz_bytes(), expected);
}

#[test]
fn block_body_execution_payload_proof() {
let value = std::fs::read_to_string(
"../test_assets/beacon/bellatrix/BeaconBlockBody/ssz_random/case_0/value.yaml",
)
.expect("cannot find test asset");
let value: Value = serde_yaml::from_str(&value).unwrap();
let content: BeaconBlockBodyBellatrix = serde_json::from_value(value).unwrap();
let expected_execution_payload_proof = [
"0xf5bf9e85dce9cc5f1edbed4085bf4e37da4ddec337483f847cc451f296ff0799",
"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x38373fc5d635131b9054c0a97cf1eeb397621f2f6e54ffc54f5f2516088b87d9",
]
.map(|x| H256::from_str(x).unwrap());
let proof = content.build_execution_payload_proof();

assert_eq!(proof.len(), 4);
assert_eq!(proof, expected_execution_payload_proof.to_vec());

let mut expected_block_hash_proof = [
"0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1",
"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b",
"0xf00e3441849a7e4228e6f48d5a5b231e153b39cb2ef283febdd9f7df1f777551",
"0x6911c0b766b06671612d77e8f3061320f2a3471c2ba8d3f8251b53da8efb111a",
]
.map(|x| H256::from_str(x).unwrap())
.to_vec();
let proof = content.build_execution_block_hash_proof();
expected_block_hash_proof.extend(expected_execution_payload_proof);

assert_eq!(proof.len(), 8);
assert_eq!(proof, expected_block_hash_proof);
}
}
Loading