Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added BlockNumber struct #1043

Merged
merged 12 commits into from
Jan 16, 2025
Merged
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

### Changes

- Added health check endpoints to the prover service (#1006).
- Implemented serialization for `AccountHeader` (#996).
- Updated Pingora crates to 0.4 and added polling time to the configuration file (#997).
- Added support for `miden-tx-prover` proxy to update workers on a running proxy (#989).
Expand All @@ -17,11 +16,13 @@
- Added conversion from `Account` to `AccountDelta` for initial account state representation as delta (#983).
- [BREAKING] Added `miden::note::get_script_hash` procedure (#995).
- [BREAKING] Refactor error messages in `miden-lib` and `miden-tx` and use `thiserror` 2.0 (#1005).
- Added health check endpoints to the prover service (#1006).
- [BREAKING] Extend `AccountId` to two `Felt`s and require block hash in derivation (#982).
- Removed workers list from the proxy configuration file (#1018).
- Added tracing to the `miden-tx-prover` CLI (#1014).
- Added metrics to the `miden-tx-prover` proxy (#1017).
- Implemented `to_hex` for `AccountIdPrefix` and `epoch_block_num` for `BlockHeader` (#1039).
- Add `BlockNumber` struct (#1043).
- Introduce `AccountIdBuilder` to simplify `AccountId` generation in tests (#1045).
- Introduced `AccountComponentTemplate` with TOML serialization and templating (#1015, #1027).
- Introduce `AccountIdError` and make account ID byte representations (`u128`, `[u8; 15]`) consistent (#1055).
Expand Down
6 changes: 4 additions & 2 deletions miden-tx/src/executor/data_store.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#[cfg(feature = "async")]
use alloc::boxed::Box;

use miden_objects::{accounts::AccountId, notes::NoteId, transaction::TransactionInputs};
use miden_objects::{
accounts::AccountId, block::BlockNumber, notes::NoteId, transaction::TransactionInputs,
};
use winter_maybe_async::*;

use crate::DataStoreError;
Expand Down Expand Up @@ -32,7 +34,7 @@ pub trait DataStore {
fn get_transaction_inputs(
&self,
account_id: AccountId,
block_ref: u32,
block_ref: BlockNumber,
notes: &[NoteId],
) -> Result<TransactionInputs, DataStoreError>;
}
3 changes: 2 additions & 1 deletion miden-tx/src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use miden_lib::transaction::TransactionKernel;
use miden_objects::{
accounts::{AccountCode, AccountId},
assembly::Library,
block::BlockNumber,
notes::NoteId,
transaction::{ExecutedTransaction, TransactionArgs, TransactionInputs},
vm::StackOutputs,
Expand Down Expand Up @@ -128,7 +129,7 @@ impl TransactionExecutor {
pub fn execute_transaction(
&self,
account_id: AccountId,
block_ref: u32,
block_ref: BlockNumber,
notes: &[NoteId],
tx_args: TransactionArgs,
) -> Result<ExecutedTransaction, TransactionExecutorError> {
Expand Down
16 changes: 8 additions & 8 deletions miden-tx/src/testing/mock_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use miden_objects::{
},
assets::{Asset, FungibleAsset, TokenSymbol},
block::{
block_num_from_epoch, compute_tx_hash, Block, BlockAccountUpdate, BlockNoteIndex,
BlockNoteTree, NoteBatch,
compute_tx_hash, Block, BlockAccountUpdate, BlockNoteIndex, BlockNoteTree, BlockNumber,
NoteBatch,
},
crypto::{
dsa::rpo_falcon512::SecretKey,
Expand Down Expand Up @@ -602,14 +602,14 @@ impl MockChain {
let block = self.blocks.last().unwrap();

let mut input_notes = vec![];
let mut block_headers_map: BTreeMap<u32, BlockHeader> = BTreeMap::new();
let mut block_headers_map: BTreeMap<BlockNumber, BlockHeader> = BTreeMap::new();
for note in notes {
let input_note = self.available_notes.get(note).expect("Note not found").clone();
let note_block_num = input_note.location().unwrap().block_num();
if note_block_num != block.header().block_num() {
block_headers_map.insert(
note_block_num,
self.blocks.get(note_block_num as usize).unwrap().header(),
self.blocks.get(note_block_num.as_usize()).unwrap().header(),
);
}
input_notes.push(input_note);
Expand All @@ -618,13 +618,13 @@ impl MockChain {
// If the account is new, add the anchor block's header from which the account ID is derived
// to the MMR.
if account.is_new() {
let epoch_block_num = block_num_from_epoch(account.id().anchor_epoch());
let epoch_block_num = BlockNumber::from_epoch(account.id().anchor_epoch());
// The reference block of the transaction is added to the MMR in
// prologue::process_chain_data so we can skip adding it to the block headers here.
if epoch_block_num != block.header().block_num() {
block_headers_map.insert(
epoch_block_num,
self.blocks.get(epoch_block_num as usize).unwrap().header(),
self.blocks.get(epoch_block_num.as_usize()).unwrap().header(),
);
}
}
Expand Down Expand Up @@ -653,7 +653,7 @@ impl MockChain {
/// This will also make all the objects currently pending available for use.
/// If `block_num` is `Some(number)`, blocks will be generated up to `number`.
pub fn seal_block(&mut self, block_num: Option<u32>) -> Block {
let next_block_num = self.blocks.last().map_or(0, |b| b.header().block_num() + 1);
let next_block_num = self.blocks.last().map_or(0, |b| b.header().block_num().child());
let target_block_num = block_num.unwrap_or(next_block_num);

if target_block_num < next_block_num {
Expand Down Expand Up @@ -711,7 +711,7 @@ impl MockChain {
let header = BlockHeader::new(
version,
prev_hash,
current_block_num,
BlockNumber::from(current_block_num),
chain_root,
account_root,
nullifier_root,
Expand Down
3 changes: 2 additions & 1 deletion miden-tx/src/testing/tx_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use miden_lib::transaction::TransactionKernel;
use miden_objects::{
accounts::{Account, AccountCode, AccountId},
assembly::Assembler,
block::BlockNumber,
notes::{Note, NoteId},
transaction::{ExecutedTransaction, InputNote, InputNotes, TransactionArgs, TransactionInputs},
};
Expand Down Expand Up @@ -139,7 +140,7 @@ impl DataStore for TransactionInputs {
fn get_transaction_inputs(
&self,
account_id: AccountId,
block_num: u32,
block_num: BlockNumber,
notes: &[NoteId],
) -> Result<TransactionInputs, DataStoreError> {
assert_eq!(account_id, self.account().id());
Expand Down
3 changes: 2 additions & 1 deletion miden-tx/src/tests/kernel_tests/test_epilogue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ fn test_block_expiration_height_monotonically_decreases() {

// Expiry block should be set to transaction's block + the stored expiration delta
// (which can only decrease, not increase)
let expected_expiry = v1.min(v2) + tx_context.tx_inputs().block_header().block_num() as u64;
let expected_expiry =
v1.min(v2) + tx_context.tx_inputs().block_header().block_num().as_u64();
assert_eq!(process.get_stack_item(8).as_int(), expected_expiry);
}
}
Expand Down
2 changes: 1 addition & 1 deletion miden-tx/src/tests/kernel_tests/test_prologue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ fn chain_mmr_memory_assertions(process: &Process<MockHost>, prepared_tx: &Transa

assert_eq!(
read_root_mem_value(process, CHAIN_MMR_NUM_LEAVES_PTR)[0],
Felt::new(chain_mmr.chain_length() as u64),
Felt::new(chain_mmr.chain_length().as_u64()),
"The number of leaves should be stored at the CHAIN_MMR_NUM_LEAVES_PTR"
);

Expand Down
14 changes: 6 additions & 8 deletions objects/src/accounts/account_id/id_anchor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::{
block::block_epoch_from_number, errors::AccountIdError, BlockHeader, Digest, EMPTY_WORD,
};
use crate::{block::BlockNumber, errors::AccountIdError, BlockHeader, Digest, EMPTY_WORD};

// ACCOUNT ID ANCHOR
// ================================================================================================
Expand All @@ -14,8 +12,8 @@ use crate::{
/// # Constraints
///
/// This type enforces the following constraints.
/// - The `anchor_block_number` % 2^[`BlockHeader::EPOCH_LENGTH_EXPONENT`] must be zero. In other
/// words, the block number must a multiple of 2^[`BlockHeader::EPOCH_LENGTH_EXPONENT`].
/// - The `anchor_block_number` % 2^[`BlockNumber::EPOCH_LENGTH_EXPONENT`] must be zero. In other
/// words, the block number must a multiple of 2^[`BlockNumber::EPOCH_LENGTH_EXPONENT`].
/// - The epoch derived from the `anchor_block_number` must be strictly less than [`u16::MAX`].
#[derive(Debug, Clone, Copy)]
pub struct AccountIdAnchor {
Expand Down Expand Up @@ -51,14 +49,14 @@ impl AccountIdAnchor {
/// Returns an error if any of the anchor constraints are not met. See the [type
/// documentation](AccountIdAnchor) for details.
pub fn new(
anchor_block_number: u32,
anchor_block_number: BlockNumber,
anchor_block_hash: Digest,
) -> Result<Self, AccountIdError> {
if anchor_block_number & 0x0000_ffff != 0 {
if anchor_block_number.as_u32() & 0x0000_ffff != 0 {
return Err(AccountIdError::AnchorBlockMustBeEpochBlock);
}

let anchor_epoch = block_epoch_from_number(anchor_block_number);
let anchor_epoch = anchor_block_number.block_epoch();

if anchor_epoch == u16::MAX {
return Err(AccountIdError::AnchorEpochMustNotBeU16Max);
Expand Down
4 changes: 2 additions & 2 deletions objects/src/accounts/account_id/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ use crate::{errors::AccountIdError, AccountError, ACCOUNT_TREE_DEPTH};
/// as the [`NoteMetadata`](crate::notes::NoteMetadata). In such cases, it can happen that all
/// bits of the encoded suffix would be one, so having the epoch constraint is important.
/// - The ID is dependent on the hash of an epoch block. This is a block whose number is a multiple
/// of 2^[`BlockHeader::EPOCH_LENGTH_EXPONENT`][epoch_len_exp], e.g. `0`, `65536`, `131072`, ...
/// of 2^[`BlockNumber::EPOCH_LENGTH_EXPONENT`][epoch_len_exp], e.g. `0`, `65536`, `131072`, ...
/// These are the first blocks of epoch 0, 1, 2, ... We call this dependence _anchoring_ because
/// the ID is anchored to that epoch block's hash. Anchoring makes it practically impossible for
/// an attacker to construct a rainbow table of account IDs whose epoch is X, if the block for
Expand All @@ -108,7 +108,7 @@ use crate::{errors::AccountIdError, AccountError, ACCOUNT_TREE_DEPTH};
/// hashes to the user's ID can claim the assets sent to the user's ID. Adding the anchor
/// block hash to ID generation process makes this attack practically impossible.
///
/// [epoch_len_exp]: crate::block::BlockHeader::EPOCH_LENGTH_EXPONENT
/// [epoch_len_exp]: crate::block::BlockNumber::EPOCH_LENGTH_EXPONENT
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccountId {
V0(AccountIdV0),
Expand Down
6 changes: 4 additions & 2 deletions objects/src/accounts/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ mod tests {
use vm_core::FieldElement;

use super::*;
use crate::accounts::StorageSlot;
use crate::{accounts::StorageSlot, block::BlockNumber};

const CUSTOM_CODE1: &str = "
export.foo
Expand Down Expand Up @@ -317,7 +317,9 @@ mod tests {

let anchor_block_hash = Digest::new([Felt::new(42); 4]);
let anchor_block_number = 1 << 16;
let id_anchor = AccountIdAnchor::new(anchor_block_number, anchor_block_hash).unwrap();
let id_anchor =
AccountIdAnchor::new(BlockNumber::from(anchor_block_number), anchor_block_hash)
.unwrap();

let (account, seed) = Account::builder([5; 32])
.anchor(id_anchor)
Expand Down
111 changes: 111 additions & 0 deletions objects/src/block/block_number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use core::{fmt, ops::Add};

use super::Felt;
use crate::utils::serde::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
};

// BLOCK NUMBER
// ================================================================================================

/// A convenience wrapper around a `u32` representing the number of a block.
///
/// Each block has a unique number and block numbers increase monotonically by `1`.
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, PartialOrd, Ord, Hash)]
pub struct BlockNumber(u32);

impl BlockNumber {
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
/// The length of an epoch expressed as a power of two. `2^(EPOCH_LENGTH_EXPONENT)` is the
/// number of blocks in an epoch.
///
/// The epoch of a block can be obtained by shifting the block number to the right by this
/// exponent.
pub const EPOCH_LENGTH_EXPONENT: u8 = 16;

/// Returns the previous block number
pub fn parent(&self) -> u32 {
self.checked_sub(1).expect("Cannot go below Genesis block!").0
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return Option<BlockNumber>. If you call BlockNumber::from(0_u32).parent() this would panic which is not what we want. The caller of parent() needs to deal with the possibility of calling this on block number 0.


/// Returns the next block number
pub fn child(&mut self) -> u32 {
self.0 + 1
}

/// Creates the [`BlockNumber`] corresponding to the epoch block for the provided `epoch`.
pub const fn from_epoch(epoch: u16) -> BlockNumber {
BlockNumber((epoch as u32) << BlockNumber::EPOCH_LENGTH_EXPONENT)
}

/// Creates a `BlockNumber` from a `usize`.
pub fn from_usize(value: usize) -> Self {
BlockNumber(value as u32)
}

/// Returns the epoch to which this block number belongs.
pub const fn block_epoch(&self) -> u16 {
(self.0 >> BlockNumber::EPOCH_LENGTH_EXPONENT) as u16
}

/// Returns the block number as a `u32`.
pub fn as_u32(&self) -> u32 {
self.0
}

/// Returns the block number as a `u64`.
pub fn as_u64(&self) -> u64 {
self.0 as u64
}

/// Returns the block number as a `usize`.
pub fn as_usize(&self) -> usize {
self.0 as usize
}

/// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
pub fn checked_sub(&self, rhs: u32) -> Option<Self> {
self.0.checked_sub(rhs).map(Self)
}
}

impl Add<u32> for BlockNumber {
type Output = Self;

fn add(self, other: u32) -> Self::Output {
BlockNumber(self.0 + other)
}
}

impl Serializable for BlockNumber {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u32(self.0);
}

fn get_size_hint(&self) -> usize {
core::mem::size_of::<u32>()
}
}

impl Deserializable for BlockNumber {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
source.read::<u32>().map(BlockNumber::from)
}
}

impl From<BlockNumber> for Felt {
fn from(value: BlockNumber) -> Self {
Felt::from(value.as_u32())
}
}

impl From<u32> for BlockNumber {
fn from(value: u32) -> Self {
BlockNumber(value)
}
}

impl fmt::Display for BlockNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
Loading
Loading