Skip to main content

miden_node_store/genesis/
mod.rs

1use anyhow::Context;
2use miden_protocol::Word;
3use miden_protocol::account::{Account, AccountPatch, AccountUpdateDetails};
4use miden_protocol::block::account_tree::{AccountIdKey, AccountTree};
5use miden_protocol::block::{
6    BlockAccountUpdate,
7    BlockBody,
8    BlockHeader,
9    BlockNoteTree,
10    BlockNumber,
11    BlockSignatures,
12    FeeParameters,
13    SignedBlock,
14    ValidatorKeys,
15};
16use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature, SigningKey};
17use miden_protocol::crypto::merkle::mmr::{Forest, MmrPeaks};
18use miden_protocol::crypto::merkle::smt::Smt;
19use miden_protocol::errors::AccountError;
20use miden_protocol::note::Nullifier;
21use miden_protocol::transaction::{OrderedTransactionHeaders, TransactionKernel};
22
23pub mod config;
24
25// GENESIS STATE
26// ================================================================================================
27
28/// Represents the state at genesis, which will be used to derive the genesis block.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct GenesisState {
31    pub accounts: Vec<Account>,
32    pub fee_parameters: FeeParameters,
33    pub version: u32,
34    pub timestamp: u32,
35    pub validator_key: PublicKey,
36}
37
38/// A type-safety wrapper ensuring that genesis block data can only be created from [`GenesisState`]
39/// or validated from a [`SignedBlock`] via [`GenesisBlock::try_from`].
40pub struct GenesisBlock(SignedBlock);
41
42/// A genesis block with all data except the validator signature.
43pub struct UnsignedGenesisBlock {
44    header: BlockHeader,
45    body: BlockBody,
46}
47
48impl UnsignedGenesisBlock {
49    pub fn header(&self) -> &BlockHeader {
50        &self.header
51    }
52
53    pub fn into_block(self, signature: Signature) -> anyhow::Result<GenesisBlock> {
54        let signatures = BlockSignatures::new(vec![signature])?;
55        signatures
56            .verify_against(self.header.commitment(), self.header.validator_keys())
57            .context("genesis block signature verification failed")?;
58
59        Ok(GenesisBlock(SignedBlock::new(self.header, self.body, signatures)?))
60    }
61}
62
63impl GenesisBlock {
64    pub fn inner(&self) -> &SignedBlock {
65        &self.0
66    }
67
68    pub fn into_inner(self) -> SignedBlock {
69        self.0
70    }
71}
72
73impl TryFrom<SignedBlock> for GenesisBlock {
74    type Error = anyhow::Error;
75
76    fn try_from(block: SignedBlock) -> anyhow::Result<Self> {
77        anyhow::ensure!(
78            block.header().block_num() == BlockNumber::GENESIS,
79            "expected genesis block number (0), got {}",
80            block.header().block_num(),
81        );
82
83        block
84            .signatures()
85            .verify_against(block.header().commitment(), block.header().validator_keys())
86            .context("genesis block signature verification failed")?;
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::Public(AccountPatch::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 validator_keys = ValidatorKeys::new(vec![self.validator_key])?;
151
152        let header = BlockHeader::new(
153            self.version,
154            Word::empty(),
155            BlockNumber::GENESIS,
156            MmrPeaks::new(Forest::empty(), Vec::new()).unwrap().hash_peaks(),
157            account_smt.root(),
158            empty_nullifier_tree.root(),
159            empty_block_note_tree.root(),
160            Word::empty(),
161            TransactionKernel.to_commitment(),
162            validator_keys,
163            self.fee_parameters,
164            self.timestamp,
165        );
166
167        let body = BlockBody::new_unchecked(
168            accounts,
169            empty_output_notes,
170            empty_nullifiers,
171            empty_transactions,
172        );
173
174        Ok(UnsignedGenesisBlock { header, body })
175    }
176
177    /// Builds and signs the genesis block with a local secret key.
178    pub fn into_block(self, signer: &SigningKey) -> anyhow::Result<GenesisBlock> {
179        let unsigned_block = self.into_unsigned_block()?;
180        let signature = signer.sign(unsigned_block.header().commitment());
181        unsigned_block.into_block(signature)
182    }
183}