hotshot_types/
stake_table.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Types and structs related to the stake table
8
9use 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    light_client::{CircuitField, StakeTableState, ToFieldsLightClientCompat},
18    traits::signature_key::{SignatureKey, StakeTableEntryType},
19    NodeType, PeerConfig,
20};
21
22/// Stake table entry
23#[derive(Serialize, Deserialize, PartialEq, Clone, Hash, Eq)]
24#[serde(bound(deserialize = ""))]
25pub struct StakeTableEntry<K: SignatureKey> {
26    /// The public key
27    pub stake_key: K,
28    /// The associated stake amount
29    pub stake_amount: U256,
30}
31
32impl<K: SignatureKey> StakeTableEntryType<K> for StakeTableEntry<K> {
33    /// Get the stake amount
34    fn stake(&self) -> U256 {
35        self.stake_amount
36    }
37
38    /// Get the public key
39    fn public_key(&self) -> K {
40        self.stake_key.clone()
41    }
42}
43
44impl<K: SignatureKey> StakeTableEntry<K> {
45    /// Get the public key
46    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
69#[inline]
70/// A helper function to compute the quorum threshold given a total amount of stake.
71pub fn one_honest_threshold(total_stake: U256) -> U256 {
72    total_stake / U256::from(3) + U256::from(1)
73}
74
75#[inline]
76/// A helper function to compute the fault tolerant quorum threshold given a total amount of stake.
77pub fn supermajority_threshold(total_stake: U256) -> U256 {
78    let one = U256::ONE;
79    let two = U256::from(2);
80    let three = U256::from(3);
81    if total_stake < U256::MAX / two {
82        ((total_stake * two) / three) + one
83    } else {
84        ((total_stake / three) * two) + two
85    }
86}
87
88#[inline]
89fn u256_to_field(amount: U256) -> CircuitField {
90    let amount_bytes: [u8; 32] = amount.to_le_bytes();
91    CircuitField::from_le_bytes_mod_order(&amount_bytes)
92}
93
94impl<TYPES: NodeType> std::iter::IntoIterator for HSStakeTable<TYPES> {
95    type Item = PeerConfig<TYPES>;
96    type IntoIter = std::vec::IntoIter<PeerConfig<TYPES>>;
97
98    fn into_iter(self) -> Self::IntoIter {
99        self.0.into_iter()
100    }
101}
102
103impl<TYPES: NodeType> HSStakeTable<TYPES> {
104    pub fn commitment(&self, stake_table_capacity: usize) -> anyhow::Result<StakeTableState> {
105        if stake_table_capacity < self.0.len() {
106            return Err(anyhow::anyhow!(
107                "Stake table over capacity: {} < {}",
108                stake_table_capacity,
109                self.0.len(),
110            ));
111        }
112        let padding_len = stake_table_capacity - self.0.len();
113        let mut bls_preimage = vec![];
114        let mut schnorr_preimage = vec![];
115        let mut amount_preimage = vec![];
116        let mut total_stake = U256::from(0);
117        for peer in &self.0 {
118            bls_preimage.extend(peer.stake_table_entry.public_key().to_fields());
119            schnorr_preimage.extend(peer.state_ver_key.to_fields());
120            amount_preimage.push(u256_to_field(peer.stake_table_entry.stake()));
121            total_stake += peer.stake_table_entry.stake();
122        }
123        bls_preimage.resize(
124            <TYPES::SignatureKey as ToFieldsLightClientCompat>::SIZE * stake_table_capacity,
125            CircuitField::default(),
126        );
127        // Nasty tech debt
128        schnorr_preimage.extend(
129            std::iter::repeat_n(TYPES::StateSignatureKey::default().to_fields(), padding_len)
130                .flatten(),
131        );
132        amount_preimage.resize(stake_table_capacity, CircuitField::default());
133        let threshold = u256_to_field(one_honest_threshold(total_stake));
134        Ok(StakeTableState {
135            bls_key_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(bls_preimage)
136                .unwrap()[0],
137            schnorr_key_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(
138                schnorr_preimage,
139            )
140            .unwrap()[0],
141            amount_comm: VariableLengthRescueCRHF::<CircuitField, 1>::evaluate(amount_preimage)
142                .unwrap()[0],
143            threshold,
144        })
145    }
146
147    pub fn total_stakes(&self) -> U256 {
148        self.0
149            .iter()
150            .map(|peer| peer.stake_table_entry.stake())
151            .sum()
152    }
153}
154
155pub struct StakeTableEntries<TYPES: NodeType>(
156    pub Vec<<<TYPES as NodeType>::SignatureKey as SignatureKey>::StakeTableEntry>,
157);
158
159impl<TYPES: NodeType> From<Vec<PeerConfig<TYPES>>> for StakeTableEntries<TYPES> {
160    fn from(peers: Vec<PeerConfig<TYPES>>) -> Self {
161        Self(
162            peers
163                .into_iter()
164                .map(|peer| peer.stake_table_entry)
165                .collect::<Vec<_>>(),
166        )
167    }
168}
169
170impl<TYPES: NodeType> From<HSStakeTable<TYPES>> for StakeTableEntries<TYPES> {
171    fn from(stake_table: HSStakeTable<TYPES>) -> Self {
172        Self::from(stake_table.0)
173    }
174}