hotshot_types/
stake_table.rs1use alloy::primitives::U256;
10use ark_ff::PrimeField;
11use derive_more::derive::{Deref, DerefMut};
12use jf_crhf::CRHF;
13use jf_rescue::crhf::VariableLengthRescueCRHF;
14use serde::{Deserialize, Serialize};
15
16use crate::{
17 NodeType, PeerConfig,
18 light_client::{CircuitField, StakeTableState, ToFieldsLightClientCompat},
19 traits::signature_key::{SignatureKey, StakeTableEntryType},
20};
21
22#[derive(Serialize, Deserialize, PartialEq, Clone, Hash, Eq)]
24#[serde(bound(deserialize = ""))]
25pub struct StakeTableEntry<K: SignatureKey> {
26 pub stake_key: K,
28 pub stake_amount: U256,
30}
31
32impl<K: SignatureKey> StakeTableEntryType<K> for StakeTableEntry<K> {
33 fn stake(&self) -> U256 {
35 self.stake_amount
36 }
37
38 fn public_key(&self) -> K {
40 self.stake_key.clone()
41 }
42}
43
44impl<K: SignatureKey> StakeTableEntry<K> {
45 pub fn key(&self) -> &K {
47 &self.stake_key
48 }
49}
50
51impl<K: SignatureKey> std::fmt::Debug for StakeTableEntry<K> {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("StakeTableEntry")
54 .field("stake_key", &format_args!("{}", self.stake_key))
55 .field("stake_amount", &self.stake_amount)
56 .finish()
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Deref, DerefMut)]
61pub struct HSStakeTable<TYPES: NodeType>(pub Vec<PeerConfig<TYPES>>);
62
63impl<TYPES: NodeType> From<Vec<PeerConfig<TYPES>>> for HSStakeTable<TYPES> {
64 fn from(peers: Vec<PeerConfig<TYPES>>) -> Self {
65 Self(peers)
66 }
67}
68
69impl<'a, T: NodeType> FromIterator<&'a PeerConfig<T>> for HSStakeTable<T> {
70 fn from_iter<I>(iter: I) -> Self
71 where
72 I: IntoIterator<Item = &'a PeerConfig<T>>,
73 {
74 Self(iter.into_iter().cloned().collect())
75 }
76}
77
78#[inline]
80pub fn one_honest_threshold(total_stake: U256) -> U256 {
81 total_stake / U256::from(3) + U256::from(1)
82}
83
84#[inline]
85pub fn supermajority_threshold(total_stake: U256) -> U256 {
87 let one = U256::ONE;
88 let two = U256::from(2);
89 let three = U256::from(3);
90 if total_stake < U256::MAX / two {
91 ((total_stake * two) / three) + one
92 } else {
93 ((total_stake / three) * two) + two
94 }
95}
96
97#[inline]
98fn u256_to_field(amount: U256) -> CircuitField {
99 let amount_bytes: [u8; 32] = amount.to_le_bytes();
100 CircuitField::from_le_bytes_mod_order(&amount_bytes)
101}
102
103impl<TYPES: NodeType> std::iter::IntoIterator for HSStakeTable<TYPES> {
104 type Item = PeerConfig<TYPES>;
105 type IntoIter = std::vec::IntoIter<PeerConfig<TYPES>>;
106
107 fn into_iter(self) -> Self::IntoIter {
108 self.0.into_iter()
109 }
110}
111
112impl<TYPES: NodeType> HSStakeTable<TYPES> {
113 pub fn commitment(&self, stake_table_capacity: usize) -> anyhow::Result<StakeTableState> {
114 if stake_table_capacity < self.0.len() {
115 return Err(anyhow::anyhow!(
116 "Stake table over capacity: {} < {}",
117 stake_table_capacity,
118 self.0.len(),
119 ));
120 }
121 let padding_len = stake_table_capacity - self.0.len();
122 let mut bls_preimage = vec![];
123 let mut schnorr_preimage = vec![];
124 let mut amount_preimage = vec![];
125 let mut total_stake = U256::from(0);
126 for peer in &self.0 {
127 bls_preimage.extend(peer.stake_table_entry.public_key().to_fields());
128 schnorr_preimage.extend(peer.state_ver_key.to_fields());
129 amount_preimage.push(u256_to_field(peer.stake_table_entry.stake()));
130 total_stake += peer.stake_table_entry.stake();
131 }
132 bls_preimage.resize(
133 <TYPES::SignatureKey as ToFieldsLightClientCompat>::SIZE * stake_table_capacity,
134 CircuitField::default(),
135 );
136 schnorr_preimage.extend(
138 std::iter::repeat_n(TYPES::StateSignatureKey::default().to_fields(), padding_len)
139 .flatten(),
140 );
141 amount_preimage.resize(stake_table_capacity, CircuitField::default());
142 let threshold = u256_to_field(one_honest_threshold(total_stake));
143 Ok(StakeTableState {
144 bls_key_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(bls_preimage)
145 .unwrap()[0],
146 schnorr_key_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(
147 schnorr_preimage,
148 )
149 .unwrap()[0],
150 amount_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(amount_preimage)
151 .unwrap()[0],
152 threshold,
153 })
154 }
155
156 pub fn total_stakes(&self) -> U256 {
157 self.0
158 .iter()
159 .map(|peer| peer.stake_table_entry.stake())
160 .sum()
161 }
162}
163
164pub struct StakeTableEntries<TYPES: NodeType>(
165 pub Vec<<<TYPES as NodeType>::SignatureKey as SignatureKey>::StakeTableEntry>,
166);
167
168impl<TYPES: NodeType> From<Vec<PeerConfig<TYPES>>> for StakeTableEntries<TYPES> {
169 fn from(peers: Vec<PeerConfig<TYPES>>) -> Self {
170 Self(
171 peers
172 .into_iter()
173 .map(|peer| peer.stake_table_entry)
174 .collect::<Vec<_>>(),
175 )
176 }
177}
178
179impl<TYPES: NodeType> From<HSStakeTable<TYPES>> for StakeTableEntries<TYPES> {
180 fn from(stake_table: HSStakeTable<TYPES>) -> Self {
181 Self::from(stake_table.0)
182 }
183}
184
185impl<'a, T: NodeType> FromIterator<&'a PeerConfig<T>> for StakeTableEntries<T> {
186 fn from_iter<I>(iter: I) -> Self
187 where
188 I: IntoIterator<Item = &'a PeerConfig<T>>,
189 {
190 Self(
191 iter.into_iter()
192 .map(|peer| peer.stake_table_entry.clone())
193 .collect(),
194 )
195 }
196}