Skip to main content

miden_node_store/genesis/
mod.rs

1use miden_protocol::Word;
2use miden_protocol::account::delta::AccountUpdateDetails;
3use miden_protocol::account::{Account, AccountDelta};
4use miden_protocol::block::account_tree::{AccountIdKey, AccountTree};
5use miden_protocol::block::{
6    BlockAccountUpdate,
7    BlockBody,
8    BlockHeader,
9    BlockNoteTree,
10    BlockNumber,
11    FeeParameters,
12    SignedBlock,
13};
14use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature, SigningKey};
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_key: PublicKey,
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`].
38pub struct GenesisBlock(SignedBlock);
39
40/// A genesis block with all data except the validator signature.
41pub struct UnsignedGenesisBlock {
42    header: BlockHeader,
43    body: BlockBody,
44}
45
46impl UnsignedGenesisBlock {
47    pub fn header(&self) -> &BlockHeader {
48        &self.header
49    }
50
51    pub fn into_block(self, signature: Signature) -> anyhow::Result<GenesisBlock> {
52        anyhow::ensure!(
53            signature.verify(self.header.commitment(), self.header.validator_key()),
54            "genesis block signature verification failed",
55        );
56
57        Ok(GenesisBlock(SignedBlock::new(self.header, self.body, signature)?))
58    }
59}
60
61impl GenesisBlock {
62    pub fn inner(&self) -> &SignedBlock {
63        &self.0
64    }
65
66    pub fn into_inner(self) -> SignedBlock {
67        self.0
68    }
69}
70
71impl TryFrom<SignedBlock> for GenesisBlock {
72    type Error = anyhow::Error;
73
74    fn try_from(block: SignedBlock) -> anyhow::Result<Self> {
75        anyhow::ensure!(
76            block.header().block_num() == BlockNumber::GENESIS,
77            "expected genesis block number (0), got {}",
78            block.header().block_num(),
79        );
80
81        anyhow::ensure!(
82            block
83                .signature()
84                .verify(block.header().commitment(), block.header().validator_key()),
85            "genesis block signature verification failed",
86        );
87
88        Ok(Self(block))
89    }
90}
91
92impl GenesisState {
93    pub fn new(
94        accounts: Vec<Account>,
95        fee_parameters: FeeParameters,
96        version: u32,
97        timestamp: u32,
98        validator_key: PublicKey,
99    ) -> Self {
100        Self {
101            accounts,
102            fee_parameters,
103            version,
104            timestamp,
105            validator_key,
106        }
107    }
108
109    /// Returns the unsigned genesis block.
110    pub fn into_unsigned_block(self) -> anyhow::Result<UnsignedGenesisBlock> {
111        let accounts: Vec<BlockAccountUpdate> = self
112            .accounts
113            .iter()
114            .map(|account| {
115                let account_update_details = if account.id().is_private() {
116                    AccountUpdateDetails::Private
117                } else {
118                    AccountUpdateDetails::Delta(AccountDelta::try_from(account.clone())?)
119                };
120
121                Ok(BlockAccountUpdate::new(
122                    account.id(),
123                    account.to_commitment(),
124                    account_update_details,
125                ))
126            })
127            .collect::<Result<Vec<_>, AccountError>>()?;
128
129        // Convert account updates to SMT entries using account_id_to_smt_key
130        let smt_entries = accounts.iter().map(|update| {
131            (
132                AccountIdKey::from(update.account_id()).as_word(),
133                update.final_state_commitment(),
134            )
135        });
136
137        let smt =
138            Smt::with_entries(smt_entries).expect("Failed to create LargeSmt for genesis accounts");
139
140        let account_smt = AccountTree::new(smt).expect("Failed to create AccountTree for genesis");
141
142        let empty_nullifiers: Vec<Nullifier> = Vec::new();
143        let empty_nullifier_tree = Smt::new();
144
145        let empty_output_notes = Vec::new();
146        let empty_block_note_tree = BlockNoteTree::empty();
147
148        let empty_transactions = OrderedTransactionHeaders::new_unchecked(Vec::new());
149
150        let header = BlockHeader::new(
151            self.version,
152            Word::empty(),
153            BlockNumber::GENESIS,
154            MmrPeaks::new(Forest::empty(), Vec::new()).unwrap().hash_peaks(),
155            account_smt.root(),
156            empty_nullifier_tree.root(),
157            empty_block_note_tree.root(),
158            Word::empty(),
159            TransactionKernel.to_commitment(),
160            self.validator_key,
161            self.fee_parameters,
162            self.timestamp,
163        );
164
165        let body = BlockBody::new_unchecked(
166            accounts,
167            empty_output_notes,
168            empty_nullifiers,
169            empty_transactions,
170        );
171
172        Ok(UnsignedGenesisBlock { header, body })
173    }
174
175    /// Builds and signs the genesis block with a local secret key.
176    pub fn into_block(self, signer: &SigningKey) -> anyhow::Result<GenesisBlock> {
177        let unsigned_block = self.into_unsigned_block()?;
178        let signature = signer.sign(unsigned_block.header().commitment());
179        unsigned_block.into_block(signature)
180    }
181}