referrerpolicy=no-referrer-when-downgrade

snowbridge_beacon_primitives/
ssz.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3use crate::{
4	config::{EXTRA_DATA_SIZE, FEE_RECIPIENT_SIZE, LOGS_BLOOM_SIZE, PUBKEY_SIZE, SIGNATURE_SIZE},
5	types::{
6		BeaconHeader, ExecutionPayloadHeader, ForkData, SigningData, SyncAggregate, SyncCommittee,
7	},
8};
9use byte_slice_cast::AsByteSlice;
10use sp_core::H256;
11use sp_std::{vec, vec::Vec};
12use ssz_rs::{
13	prelude::{List, Vector},
14	Bitvector, Deserialize, DeserializeError, SimpleSerialize, SimpleSerializeError, Sized, U256,
15};
16use ssz_rs_derive::SimpleSerialize as SimpleSerializeDerive;
17
18#[derive(Default, SimpleSerializeDerive, Clone, Debug)]
19pub struct SSZBeaconBlockHeader {
20	pub slot: u64,
21	pub proposer_index: u64,
22	pub parent_root: [u8; 32],
23	pub state_root: [u8; 32],
24	pub body_root: [u8; 32],
25}
26
27impl From<BeaconHeader> for SSZBeaconBlockHeader {
28	fn from(beacon_header: BeaconHeader) -> Self {
29		SSZBeaconBlockHeader {
30			slot: beacon_header.slot,
31			proposer_index: beacon_header.proposer_index,
32			parent_root: beacon_header.parent_root.to_fixed_bytes(),
33			state_root: beacon_header.state_root.to_fixed_bytes(),
34			body_root: beacon_header.body_root.to_fixed_bytes(),
35		}
36	}
37}
38
39#[derive(Default, SimpleSerializeDerive, Clone)]
40pub struct SSZSyncCommittee<const COMMITTEE_SIZE: usize> {
41	pub pubkeys: Vector<Vector<u8, PUBKEY_SIZE>, COMMITTEE_SIZE>,
42	pub aggregate_pubkey: Vector<u8, PUBKEY_SIZE>,
43}
44
45impl<const COMMITTEE_SIZE: usize> From<SyncCommittee<COMMITTEE_SIZE>>
46	for SSZSyncCommittee<COMMITTEE_SIZE>
47{
48	fn from(sync_committee: SyncCommittee<COMMITTEE_SIZE>) -> Self {
49		let mut pubkeys_vec = Vec::new();
50
51		for pubkey in sync_committee.pubkeys.iter() {
52			// The only thing that can go wrong in the conversion from vec to Vector (ssz type) is
53			// that the Vector size is 0, or that the given data to create the Vector from does not
54			// match the expected size N. Because these sizes are statically checked (i.e.
55			// PublicKey's size is 48, and const PUBKEY_SIZE is 48, it is impossible for "try_from"
56			// to return an error condition.
57			let conv_pubkey = Vector::<u8, PUBKEY_SIZE>::try_from(pubkey.0.to_vec())
58				.expect("checked statically; qed");
59
60			pubkeys_vec.push(conv_pubkey);
61		}
62
63		let pubkeys = Vector::<Vector<u8, PUBKEY_SIZE>, { COMMITTEE_SIZE }>::try_from(pubkeys_vec)
64			.expect("checked statically; qed");
65
66		let aggregate_pubkey =
67			Vector::<u8, PUBKEY_SIZE>::try_from(sync_committee.aggregate_pubkey.0.to_vec())
68				.expect("checked statically; qed");
69
70		SSZSyncCommittee { pubkeys, aggregate_pubkey }
71	}
72}
73
74#[derive(Default, Debug, SimpleSerializeDerive, Clone)]
75pub struct SSZSyncAggregate<const COMMITTEE_SIZE: usize> {
76	pub sync_committee_bits: Bitvector<COMMITTEE_SIZE>,
77	pub sync_committee_signature: Vector<u8, SIGNATURE_SIZE>,
78}
79
80impl<const COMMITTEE_SIZE: usize, const COMMITTEE_BITS_SIZE: usize>
81	From<SyncAggregate<COMMITTEE_SIZE, COMMITTEE_BITS_SIZE>> for SSZSyncAggregate<COMMITTEE_SIZE>
82{
83	fn from(sync_aggregate: SyncAggregate<COMMITTEE_SIZE, COMMITTEE_BITS_SIZE>) -> Self {
84		SSZSyncAggregate {
85			sync_committee_bits: Bitvector::<COMMITTEE_SIZE>::deserialize(
86				&sync_aggregate.sync_committee_bits,
87			)
88			.expect("checked statically; qed"),
89			sync_committee_signature: Vector::<u8, SIGNATURE_SIZE>::try_from(
90				sync_aggregate.sync_committee_signature.0.to_vec(),
91			)
92			.expect("checked statically; qed"),
93		}
94	}
95}
96
97#[derive(Default, SimpleSerializeDerive, Clone)]
98pub struct SSZForkData {
99	pub current_version: [u8; 4],
100	pub genesis_validators_root: [u8; 32],
101}
102
103impl From<ForkData> for SSZForkData {
104	fn from(fork_data: ForkData) -> Self {
105		SSZForkData {
106			current_version: fork_data.current_version,
107			genesis_validators_root: fork_data.genesis_validators_root,
108		}
109	}
110}
111
112#[derive(Default, SimpleSerializeDerive, Clone)]
113pub struct SSZSigningData {
114	pub object_root: [u8; 32],
115	pub domain: [u8; 32],
116}
117
118impl From<SigningData> for SSZSigningData {
119	fn from(signing_data: SigningData) -> Self {
120		SSZSigningData {
121			object_root: signing_data.object_root.into(),
122			domain: signing_data.domain.into(),
123		}
124	}
125}
126
127#[derive(Default, SimpleSerializeDerive, Clone, Debug)]
128pub struct SSZExecutionPayloadHeader {
129	pub parent_hash: [u8; 32],
130	pub fee_recipient: Vector<u8, FEE_RECIPIENT_SIZE>,
131	pub state_root: [u8; 32],
132	pub receipts_root: [u8; 32],
133	pub logs_bloom: Vector<u8, LOGS_BLOOM_SIZE>,
134	pub prev_randao: [u8; 32],
135	pub block_number: u64,
136	pub gas_limit: u64,
137	pub gas_used: u64,
138	pub timestamp: u64,
139	pub extra_data: List<u8, EXTRA_DATA_SIZE>,
140	pub base_fee_per_gas: U256,
141	pub block_hash: [u8; 32],
142	pub transactions_root: [u8; 32],
143	pub withdrawals_root: [u8; 32],
144}
145
146impl TryFrom<ExecutionPayloadHeader> for SSZExecutionPayloadHeader {
147	type Error = SimpleSerializeError;
148
149	fn try_from(payload: ExecutionPayloadHeader) -> Result<Self, Self::Error> {
150		Ok(SSZExecutionPayloadHeader {
151			parent_hash: payload.parent_hash.to_fixed_bytes(),
152			fee_recipient: Vector::<u8, FEE_RECIPIENT_SIZE>::try_from(
153				payload.fee_recipient.to_fixed_bytes().to_vec(),
154			)
155			.expect("checked statically; qed"),
156			state_root: payload.state_root.to_fixed_bytes(),
157			receipts_root: payload.receipts_root.to_fixed_bytes(),
158			// Logs bloom bytes size is not constrained, so here we do need to check the try_from
159			// error
160			logs_bloom: Vector::<u8, LOGS_BLOOM_SIZE>::try_from(payload.logs_bloom)
161				.map_err(|(_, err)| err)?,
162			prev_randao: payload.prev_randao.to_fixed_bytes(),
163			block_number: payload.block_number,
164			gas_limit: payload.gas_limit,
165			gas_used: payload.gas_used,
166			timestamp: payload.timestamp,
167			// Extra data bytes size is not constrained, so here we do need to check the try_from
168			// error
169			extra_data: List::<u8, EXTRA_DATA_SIZE>::try_from(payload.extra_data)
170				.map_err(|(_, err)| err)?,
171			base_fee_per_gas: U256::from_bytes_le(
172				payload
173					.base_fee_per_gas
174					.as_byte_slice()
175					.try_into()
176					.expect("checked in prep; qed"),
177			),
178			block_hash: payload.block_hash.to_fixed_bytes(),
179			transactions_root: payload.transactions_root.to_fixed_bytes(),
180			withdrawals_root: payload.withdrawals_root.to_fixed_bytes(),
181		})
182	}
183}
184
185pub fn hash_tree_root<T: SimpleSerialize>(mut object: T) -> Result<H256, SimpleSerializeError> {
186	match object.hash_tree_root() {
187		Ok(node) => {
188			let fixed_bytes: [u8; 32] =
189				node.as_ref().try_into().expect("Node is a newtype over [u8; 32]; qed");
190			Ok(fixed_bytes.into())
191		},
192		Err(err) => Err(err.into()),
193	}
194}
195
196pub mod deneb {
197	use crate::{
198		config::{EXTRA_DATA_SIZE, FEE_RECIPIENT_SIZE, LOGS_BLOOM_SIZE},
199		ssz::hash_tree_root,
200		types::deneb::ExecutionPayloadHeader,
201	};
202	use byte_slice_cast::AsByteSlice;
203	use sp_core::H256;
204	use sp_std::{vec, vec::Vec};
205	use ssz_rs::{
206		prelude::{List, Vector},
207		Deserialize, DeserializeError, SimpleSerializeError, Sized, U256,
208	};
209	use ssz_rs_derive::SimpleSerialize as SimpleSerializeDerive;
210
211	#[derive(Default, SimpleSerializeDerive, Clone, Debug)]
212	pub struct SSZExecutionPayloadHeader {
213		pub parent_hash: [u8; 32],
214		pub fee_recipient: Vector<u8, FEE_RECIPIENT_SIZE>,
215		pub state_root: [u8; 32],
216		pub receipts_root: [u8; 32],
217		pub logs_bloom: Vector<u8, LOGS_BLOOM_SIZE>,
218		pub prev_randao: [u8; 32],
219		pub block_number: u64,
220		pub gas_limit: u64,
221		pub gas_used: u64,
222		pub timestamp: u64,
223		pub extra_data: List<u8, EXTRA_DATA_SIZE>,
224		pub base_fee_per_gas: U256,
225		pub block_hash: [u8; 32],
226		pub transactions_root: [u8; 32],
227		pub withdrawals_root: [u8; 32],
228		pub blob_gas_used: u64,
229		pub excess_blob_gas: u64,
230	}
231
232	impl TryFrom<ExecutionPayloadHeader> for SSZExecutionPayloadHeader {
233		type Error = SimpleSerializeError;
234
235		fn try_from(payload: ExecutionPayloadHeader) -> Result<Self, Self::Error> {
236			Ok(SSZExecutionPayloadHeader {
237				parent_hash: payload.parent_hash.to_fixed_bytes(),
238				fee_recipient: Vector::<u8, FEE_RECIPIENT_SIZE>::try_from(
239					payload.fee_recipient.to_fixed_bytes().to_vec(),
240				)
241				.expect("checked statically; qed"),
242				state_root: payload.state_root.to_fixed_bytes(),
243				receipts_root: payload.receipts_root.to_fixed_bytes(),
244				// Logs bloom bytes size is not constrained, so here we do need to check the
245				// try_from error
246				logs_bloom: Vector::<u8, LOGS_BLOOM_SIZE>::try_from(payload.logs_bloom)
247					.map_err(|(_, err)| err)?,
248				prev_randao: payload.prev_randao.to_fixed_bytes(),
249				block_number: payload.block_number,
250				gas_limit: payload.gas_limit,
251				gas_used: payload.gas_used,
252				timestamp: payload.timestamp,
253				// Extra data bytes size is not constrained, so here we do need to check the
254				// try_from error
255				extra_data: List::<u8, EXTRA_DATA_SIZE>::try_from(payload.extra_data)
256					.map_err(|(_, err)| err)?,
257				base_fee_per_gas: U256::from_bytes_le(
258					payload
259						.base_fee_per_gas
260						.as_byte_slice()
261						.try_into()
262						.expect("checked in prep; qed"),
263				),
264				block_hash: payload.block_hash.to_fixed_bytes(),
265				transactions_root: payload.transactions_root.to_fixed_bytes(),
266				withdrawals_root: payload.withdrawals_root.to_fixed_bytes(),
267				blob_gas_used: payload.blob_gas_used,
268				excess_blob_gas: payload.excess_blob_gas,
269			})
270		}
271	}
272
273	impl ExecutionPayloadHeader {
274		pub fn hash_tree_root(&self) -> Result<H256, SimpleSerializeError> {
275			hash_tree_root::<SSZExecutionPayloadHeader>(self.clone().try_into()?)
276		}
277	}
278}