Skip to main content

miden_node_store/genesis/
mod.rs

1use miden_protocol::Word;
2use miden_protocol::account::{Account, AccountPatch, AccountUpdateDetails};
3use miden_protocol::block::account_tree::{AccountIdKey, AccountTree};
4use miden_protocol::block::{
5    BlockAccountUpdate,
6    BlockBody,
7    BlockHeader,
8    BlockNoteTree,
9    BlockNumber,
10    BlockSignatures,
11    FeeParameters,
12    SignedBlock,
13    ValidatorKeys,
14};
15use miden_protocol::crypto::merkle::mmr::{Forest, MmrPeaks};
16use miden_protocol::crypto::merkle::smt::Smt;
17use miden_protocol::errors::AccountError;
18use miden_protocol::note::Nullifier;
19use miden_protocol::transaction::{OrderedTransactionHeaders, TransactionKernel};
20
21pub mod config;
22
23// GENESIS STATE
24// ================================================================================================
25
26/// Represents the state at genesis, which will be used to derive the genesis block.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct GenesisState {
29    pub accounts: Vec<Account>,
30    pub fee_parameters: FeeParameters,
31    pub version: u32,
32    pub timestamp: u32,
33    pub validator_keys: ValidatorKeys,
34}
35
36/// A type-safety wrapper ensuring that genesis block data can only be created from [`GenesisState`]
37/// or validated from a [`SignedBlock`] via [`GenesisBlock::try_from`].
38#[derive(Debug)]
39pub struct GenesisBlock(SignedBlock);
40
41impl GenesisBlock {
42    pub fn inner(&self) -> &SignedBlock {
43        &self.0
44    }
45
46    pub fn into_inner(self) -> SignedBlock {
47        self.0
48    }
49}
50
51impl TryFrom<SignedBlock> for GenesisBlock {
52    type Error = anyhow::Error;
53
54    fn try_from(block: SignedBlock) -> anyhow::Result<Self> {
55        anyhow::ensure!(
56            block.header().block_num() == BlockNumber::GENESIS,
57            "expected genesis block number (0), got {}",
58            block.header().block_num(),
59        );
60
61        // The genesis block has no parent and is not signed: it acts as the chain's trust root and
62        // must be obtained from a trusted source. Its header commits to the validator set, which is
63        // required to sign every block after genesis.
64        anyhow::ensure!(
65            block.signatures().is_empty(),
66            "genesis block must not carry signatures, got {}",
67            block.signatures().len(),
68        );
69
70        Ok(Self(block))
71    }
72}
73
74impl GenesisState {
75    pub fn new(
76        accounts: Vec<Account>,
77        fee_parameters: FeeParameters,
78        version: u32,
79        timestamp: u32,
80        validator_keys: ValidatorKeys,
81    ) -> Self {
82        Self {
83            accounts,
84            fee_parameters,
85            version,
86            timestamp,
87            validator_keys,
88        }
89    }
90
91    /// Builds the genesis block.
92    ///
93    /// The genesis block has no parent and is not signed: it acts as the chain's trust root and
94    /// must be obtained from a trusted source. Its header commits to the validator set, which is
95    /// required to sign every block after genesis.
96    pub fn into_block(self) -> anyhow::Result<GenesisBlock> {
97        let accounts: Vec<BlockAccountUpdate> = self
98            .accounts
99            .iter()
100            .map(|account| {
101                let account_update_details = if account.id().is_private() {
102                    AccountUpdateDetails::Private
103                } else {
104                    AccountUpdateDetails::Public(AccountPatch::try_from(account.clone())?)
105                };
106
107                Ok(BlockAccountUpdate::new(
108                    account.id(),
109                    account.to_commitment(),
110                    account_update_details,
111                ))
112            })
113            .collect::<Result<Vec<_>, AccountError>>()?;
114
115        // Convert account updates to SMT entries using account_id_to_smt_key
116        let smt_entries = accounts.iter().map(|update| {
117            (
118                AccountIdKey::from(update.account_id()).as_word(),
119                update.final_state_commitment(),
120            )
121        });
122
123        let smt =
124            Smt::with_entries(smt_entries).expect("Failed to create LargeSmt for genesis accounts");
125
126        let account_smt = AccountTree::new(smt).expect("Failed to create AccountTree for genesis");
127
128        let empty_nullifiers: Vec<Nullifier> = Vec::new();
129        let empty_nullifier_tree = Smt::new();
130
131        let empty_output_notes = Vec::new();
132        let empty_block_note_tree = BlockNoteTree::empty();
133
134        let empty_transactions = OrderedTransactionHeaders::new_unchecked(Vec::new());
135
136        let validator_keys = self.validator_keys;
137
138        let header = BlockHeader::new(
139            self.version,
140            Word::empty(),
141            BlockNumber::GENESIS,
142            MmrPeaks::new(Forest::empty(), Vec::new()).unwrap().hash_peaks(),
143            account_smt.root(),
144            empty_nullifier_tree.root(),
145            empty_block_note_tree.root(),
146            Word::empty(),
147            TransactionKernel.to_commitment(),
148            validator_keys,
149            self.fee_parameters,
150            self.timestamp,
151        );
152
153        let body = BlockBody::new_unchecked(
154            accounts,
155            empty_output_notes,
156            empty_nullifiers,
157            empty_transactions,
158        );
159
160        let signatures = BlockSignatures::new(Vec::new())
161            .map_err(|err| anyhow::anyhow!("failed to build empty genesis signatures: {err}"))?;
162
163        Ok(GenesisBlock(SignedBlock::new(header, body, signatures)?))
164    }
165}