use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound};
use scale_info::TypeInfo;
use sp_core::{H160, H256, U256};
use sp_runtime::RuntimeDebug;
use sp_std::{boxed::Box, iter::repeat, prelude::*};
use crate::config::{PUBKEY_SIZE, SIGNATURE_SIZE};
#[cfg(feature = "std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "std")]
use crate::serde_utils::HexVisitor;
use crate::ssz::{
hash_tree_root, SSZBeaconBlockHeader, SSZExecutionPayloadHeader, SSZForkData, SSZSigningData,
SSZSyncAggregate, SSZSyncCommittee,
};
use ssz_rs::SimpleSerializeError;
pub use crate::bits::decompress_sync_committee_bits;
use crate::bls::{prepare_g1_pubkeys, prepare_milagro_pubkey, BlsError};
use milagro_bls::PublicKey as PublicKeyPrepared;
pub type ValidatorIndex = u64;
pub type ForkVersion = [u8; 4];
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct ForkVersions {
pub genesis: Fork,
pub altair: Fork,
pub bellatrix: Fork,
pub capella: Fork,
pub deneb: Fork,
}
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct Fork {
pub version: [u8; 4],
pub epoch: u64,
}
#[derive(Copy, Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct PublicKey(pub [u8; PUBKEY_SIZE]);
impl Default for PublicKey {
fn default() -> Self {
PublicKey([0u8; PUBKEY_SIZE])
}
}
impl From<[u8; PUBKEY_SIZE]> for PublicKey {
fn from(v: [u8; PUBKEY_SIZE]) -> Self {
Self(v)
}
}
impl MaxEncodedLen for PublicKey {
fn max_encoded_len() -> usize {
PUBKEY_SIZE
}
}
#[cfg(feature = "std")]
impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(HexVisitor::<PUBKEY_SIZE>()).map(|v| v.into())
}
}
#[cfg(feature = "std")]
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.0)
}
}
#[derive(Copy, Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct Signature(pub [u8; SIGNATURE_SIZE]);
impl Default for Signature {
fn default() -> Self {
Signature([0u8; SIGNATURE_SIZE])
}
}
impl From<[u8; SIGNATURE_SIZE]> for Signature {
fn from(v: [u8; SIGNATURE_SIZE]) -> Self {
Self(v)
}
}
#[cfg(feature = "std")]
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(HexVisitor::<SIGNATURE_SIZE>()).map(|v| v.into())
}
}
#[derive(Copy, Clone, Default, Encode, Decode, TypeInfo, MaxEncodedLen)]
pub struct FinalizedHeaderState {
pub beacon_block_root: H256,
pub beacon_slot: u64,
}
#[derive(Clone, Default, Encode, Decode, PartialEq, RuntimeDebug)]
pub struct ForkData {
pub current_version: [u8; 4],
pub genesis_validators_root: [u8; 32],
}
impl ForkData {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZForkData>(self.clone().into())
}
}
#[derive(Clone, Default, Encode, Decode, PartialEq, RuntimeDebug)]
pub struct SigningData {
pub object_root: H256,
pub domain: H256,
}
impl SigningData {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZSigningData>(self.clone().into())
}
}
#[derive(
Encode, Decode, PartialEqNoBound, CloneNoBound, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(
feature = "std",
derive(Serialize, Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
#[codec(mel_bound())]
pub struct SyncCommittee<const COMMITTEE_SIZE: usize> {
#[cfg_attr(feature = "std", serde(with = "crate::serde_utils::arrays"))]
pub pubkeys: [PublicKey; COMMITTEE_SIZE],
pub aggregate_pubkey: PublicKey,
}
impl<const COMMITTEE_SIZE: usize> Default for SyncCommittee<COMMITTEE_SIZE> {
fn default() -> Self {
SyncCommittee {
pubkeys: [Default::default(); COMMITTEE_SIZE],
aggregate_pubkey: Default::default(),
}
}
}
impl<const COMMITTEE_SIZE: usize> SyncCommittee<COMMITTEE_SIZE> {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZSyncCommittee<COMMITTEE_SIZE>>(self.clone().into())
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)]
pub struct SyncCommitteePrepared<const COMMITTEE_SIZE: usize> {
pub root: H256,
pub pubkeys: Box<[PublicKeyPrepared; COMMITTEE_SIZE]>,
pub aggregate_pubkey: PublicKeyPrepared,
}
impl<const COMMITTEE_SIZE: usize> Default for SyncCommitteePrepared<COMMITTEE_SIZE> {
fn default() -> Self {
let pubkeys: Vec<PublicKeyPrepared> =
repeat(PublicKeyPrepared::default()).take(COMMITTEE_SIZE).collect();
let pubkeys: Box<[PublicKeyPrepared; COMMITTEE_SIZE]> =
Box::new(pubkeys.try_into().map_err(|_| ()).expect("checked statically; qed"));
SyncCommitteePrepared {
root: H256::default(),
pubkeys,
aggregate_pubkey: PublicKeyPrepared::default(),
}
}
}
impl<const COMMITTEE_SIZE: usize> TryFrom<&SyncCommittee<COMMITTEE_SIZE>>
for SyncCommitteePrepared<COMMITTEE_SIZE>
{
type Error = BlsError;
fn try_from(sync_committee: &SyncCommittee<COMMITTEE_SIZE>) -> Result<Self, Self::Error> {
let g1_pubkeys = prepare_g1_pubkeys(&sync_committee.pubkeys)?;
let sync_committee_root = sync_committee.hash_tree_root().expect("checked statically; qed");
Ok(SyncCommitteePrepared::<COMMITTEE_SIZE> {
pubkeys: g1_pubkeys.try_into().map_err(|_| ()).expect("checked statically; qed"),
aggregate_pubkey: prepare_milagro_pubkey(&sync_committee.aggregate_pubkey)?,
root: sync_committee_root,
})
}
}
#[derive(
Copy, Clone, Default, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct BeaconHeader {
pub slot: u64,
pub proposer_index: ValidatorIndex,
pub parent_root: H256,
pub state_root: H256,
pub body_root: H256,
}
impl BeaconHeader {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZBeaconBlockHeader>((*self).into())
}
}
#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)]
#[cfg_attr(
feature = "std",
derive(Deserialize),
serde(
try_from = "IntermediateSyncAggregate",
deny_unknown_fields,
bound(serialize = ""),
bound(deserialize = "")
)
)]
#[codec(mel_bound())]
pub struct SyncAggregate<const COMMITTEE_SIZE: usize, const COMMITTEE_BITS_SIZE: usize> {
pub sync_committee_bits: [u8; COMMITTEE_BITS_SIZE],
pub sync_committee_signature: Signature,
}
impl<const COMMITTEE_SIZE: usize, const COMMITTEE_BITS_SIZE: usize> Default
for SyncAggregate<COMMITTEE_SIZE, COMMITTEE_BITS_SIZE>
{
fn default() -> Self {
SyncAggregate {
sync_committee_bits: [0; COMMITTEE_BITS_SIZE],
sync_committee_signature: Default::default(),
}
}
}
impl<const COMMITTEE_SIZE: usize, const COMMITTEE_BITS_SIZE: usize>
SyncAggregate<COMMITTEE_SIZE, COMMITTEE_BITS_SIZE>
{
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZSyncAggregate<COMMITTEE_SIZE>>(self.clone().into())
}
}
#[cfg(feature = "std")]
#[derive(Deserialize)]
struct IntermediateSyncAggregate {
#[cfg_attr(feature = "std", serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes"))]
pub sync_committee_bits: Vec<u8>,
pub sync_committee_signature: Signature,
}
#[cfg(feature = "std")]
impl<const COMMITTEE_SIZE: usize, const COMMITTEE_BITS_SIZE: usize>
TryFrom<IntermediateSyncAggregate> for SyncAggregate<COMMITTEE_SIZE, COMMITTEE_BITS_SIZE>
{
type Error = String;
fn try_from(other: IntermediateSyncAggregate) -> Result<Self, Self::Error> {
Ok(Self {
sync_committee_bits: other
.sync_committee_bits
.try_into()
.map_err(|_| "unexpected length".to_owned())?,
sync_committee_signature: other.sync_committee_signature,
})
}
}
#[derive(
Default, Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
)]
#[cfg_attr(
feature = "std",
derive(Serialize, Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
#[codec(mel_bound())]
pub struct ExecutionPayloadHeader {
pub parent_hash: H256,
pub fee_recipient: H160,
pub state_root: H256,
pub receipts_root: H256,
#[cfg_attr(feature = "std", serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes"))]
pub logs_bloom: Vec<u8>,
pub prev_randao: H256,
pub block_number: u64,
pub gas_limit: u64,
pub gas_used: u64,
pub timestamp: u64,
#[cfg_attr(feature = "std", serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes"))]
pub extra_data: Vec<u8>,
#[cfg_attr(feature = "std", serde(deserialize_with = "crate::serde_utils::from_int_to_u256"))]
pub base_fee_per_gas: U256,
pub block_hash: H256,
pub transactions_root: H256,
pub withdrawals_root: H256,
}
impl ExecutionPayloadHeader {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
hash_tree_root::<SSZExecutionPayloadHeader>(self.clone().try_into()?)
}
}
#[derive(
Default,
Encode,
Decode,
Copy,
Clone,
PartialEqNoBound,
RuntimeDebugNoBound,
TypeInfo,
MaxEncodedLen,
)]
pub struct CompactBeaconState {
#[codec(compact)]
pub slot: u64,
pub block_roots_root: H256,
}
#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)]
#[cfg_attr(
feature = "std",
derive(Serialize, Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
#[codec(mel_bound())]
pub enum VersionedExecutionPayloadHeader {
Capella(ExecutionPayloadHeader),
Deneb(deneb::ExecutionPayloadHeader),
}
impl VersionedExecutionPayloadHeader {
pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
match self {
VersionedExecutionPayloadHeader::Capella(execution_payload_header) =>
hash_tree_root::<SSZExecutionPayloadHeader>(
execution_payload_header.clone().try_into()?,
),
VersionedExecutionPayloadHeader::Deneb(execution_payload_header) =>
hash_tree_root::<crate::ssz::deneb::SSZExecutionPayloadHeader>(
execution_payload_header.clone().try_into()?,
),
}
}
pub fn block_hash(&self) -> H256 {
match self {
VersionedExecutionPayloadHeader::Capella(execution_payload_header) =>
execution_payload_header.block_hash,
VersionedExecutionPayloadHeader::Deneb(execution_payload_header) =>
execution_payload_header.block_hash,
}
}
pub fn block_number(&self) -> u64 {
match self {
VersionedExecutionPayloadHeader::Capella(execution_payload_header) =>
execution_payload_header.block_number,
VersionedExecutionPayloadHeader::Deneb(execution_payload_header) =>
execution_payload_header.block_number,
}
}
pub fn receipts_root(&self) -> H256 {
match self {
VersionedExecutionPayloadHeader::Capella(execution_payload_header) =>
execution_payload_header.receipts_root,
VersionedExecutionPayloadHeader::Deneb(execution_payload_header) =>
execution_payload_header.receipts_root,
}
}
}
#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)]
#[cfg_attr(
feature = "std",
derive(serde::Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
pub struct ExecutionProof {
pub header: BeaconHeader,
pub ancestry_proof: Option<AncestryProof>,
pub execution_header: VersionedExecutionPayloadHeader,
pub execution_branch: Vec<H256>,
}
#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)]
#[cfg_attr(
feature = "std",
derive(serde::Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
pub struct AncestryProof {
pub header_branch: Vec<H256>,
pub finalized_block_root: H256,
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
#[test]
pub fn test_hash_beacon_header1() {
let hash_root = BeaconHeader {
slot: 3,
proposer_index: 2,
parent_root: hex!("796ea53efb534eab7777809cc5ee2d84e7f25024b9d0c4d7e5bcaab657e4bdbd")
.into(),
state_root: hex!("ba3ff080912be5c9c158b2e962c1b39a91bc0615762ba6fa2ecacafa94e9ae0a")
.into(),
body_root: hex!("a18d7fcefbb74a177c959160e0ee89c23546482154e6831237710414465dcae5")
.into(),
}
.hash_tree_root();
assert!(hash_root.is_ok());
assert_eq!(
hash_root.unwrap(),
hex!("7d42595818709e805dd2fa710a2d2c1f62576ef1ab7273941ac9130fb94b91f7").into()
);
}
#[test]
pub fn test_hash_beacon_header2() {
let hash_root = BeaconHeader {
slot: 3476424,
proposer_index: 314905,
parent_root: hex!("c069d7b49cffd2b815b0fb8007eb9ca91202ea548df6f3db60000f29b2489f28")
.into(),
state_root: hex!("444d293e4533501ee508ad608783a7d677c3c566f001313e8a02ce08adf590a3")
.into(),
body_root: hex!("6508a0241047f21ba88f05d05b15534156ab6a6f8e029a9a5423da429834e04a")
.into(),
}
.hash_tree_root();
assert!(hash_root.is_ok());
assert_eq!(
hash_root.unwrap(),
hex!("0aa41166ff01e58e111ac8c42309a738ab453cf8d7285ed8477b1c484acb123e").into()
);
}
#[test]
pub fn test_hash_fork_data() {
let hash_root = ForkData {
current_version: hex!("83f38a34"),
genesis_validators_root: hex!(
"22370bbbb358800f5711a10ea9845284272d8493bed0348cab87b8ab1e127930"
),
}
.hash_tree_root();
assert!(hash_root.is_ok());
assert_eq!(
hash_root.unwrap(),
hex!("57c12c4246bc7152b174b51920506bf943eff9c7ffa50b9533708e9cc1f680fc").into()
);
}
#[test]
pub fn test_hash_signing_data() {
let hash_root = SigningData {
object_root: hex!("63654cbe64fc07853f1198c165dd3d49c54fc53bc417989bbcc66da15f850c54")
.into(),
domain: hex!("037da907d1c3a03c0091b2254e1480d9b1783476e228ab29adaaa8f133e08f7a").into(),
}
.hash_tree_root();
assert!(hash_root.is_ok());
assert_eq!(
hash_root.unwrap(),
hex!("b9eb2caf2d691b183c2d57f322afe505c078cd08101324f61c3641714789a54e").into()
);
}
#[test]
pub fn test_hash_sync_aggregate() {
let hash_root = SyncAggregate::<512, 64>{
sync_committee_bits: hex!("cefffffefffffff767fffbedffffeffffeeffdffffdebffffff7f7dbdf7fffdffffbffcfffdff79dfffbbfefff2ffffff7ddeff7ffffc98ff7fbfffffffffff7"),
sync_committee_signature: hex!("8af1a8577bba419fe054ee49b16ed28e081dda6d3ba41651634685e890992a0b675e20f8d9f2ec137fe9eb50e838aa6117f9f5410e2e1024c4b4f0e098e55144843ce90b7acde52fe7b94f2a1037342c951dc59f501c92acf7ed944cb6d2b5f7").into(),
}.hash_tree_root();
assert!(hash_root.is_ok());
assert_eq!(
hash_root.unwrap(),
hex!("e6dcad4f60ce9ff8a587b110facbaf94721f06cd810b6d8bf6cffa641272808d").into()
);
}
#[test]
pub fn test_hash_execution_payload() {
let hash_root =
ExecutionPayloadHeader{
parent_hash: hex!("eadee5ab098dde64e9fd02ae5858064bad67064070679625b09f8d82dec183f7").into(),
fee_recipient: hex!("f97e180c050e5ab072211ad2c213eb5aee4df134").into(),
state_root: hex!("564fa064c2a324c2b5978d7fdfc5d4224d4f421a45388af1ed405a399c845dff").into(),
receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(),
prev_randao: hex!("6bf538bdfbdf1c96ff528726a40658a91d0bda0f1351448c4c4f3604db2a0ccf").into(),
block_number: 477434,
gas_limit: 8154925,
gas_used: 0,
timestamp: 1652816940,
extra_data: vec![],
base_fee_per_gas: U256::from(7_i16),
block_hash: hex!("cd8df91b4503adb8f2f1c7a4f60e07a1f1a2cbdfa2a95bceba581f3ff65c1968").into(),
transactions_root: hex!("7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1").into(),
withdrawals_root: hex!("28ba1834a3a7b657460ce79fa3a1d909ab8828fd557659d4d0554a9bdbc0ec30").into(),
}.hash_tree_root();
assert!(hash_root.is_ok());
}
}
#[derive(Encode, Decode, Copy, Clone, PartialEq, RuntimeDebug, TypeInfo)]
pub enum Mode {
Active,
Blocked,
}
pub mod deneb {
use codec::{Decode, Encode};
use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_core::{H160, H256, U256};
use sp_std::prelude::*;
#[derive(
Default, Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
)]
#[cfg_attr(
feature = "std",
derive(Serialize, Deserialize),
serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
)]
#[codec(mel_bound())]
pub struct ExecutionPayloadHeader {
pub parent_hash: H256,
pub fee_recipient: H160,
pub state_root: H256,
pub receipts_root: H256,
#[cfg_attr(
feature = "std",
serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes")
)]
pub logs_bloom: Vec<u8>,
pub prev_randao: H256,
pub block_number: u64,
pub gas_limit: u64,
pub gas_used: u64,
pub timestamp: u64,
#[cfg_attr(
feature = "std",
serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes")
)]
pub extra_data: Vec<u8>,
#[cfg_attr(
feature = "std",
serde(deserialize_with = "crate::serde_utils::from_int_to_u256")
)]
pub base_fee_per_gas: U256,
pub block_hash: H256,
pub transactions_root: H256,
pub withdrawals_root: H256,
pub blob_gas_used: u64, pub excess_blob_gas: u64, }
}