Skip to content

Commit

Permalink
feat: add Block object and refactored AccountUpdate object (#621)
Browse files Browse the repository at this point in the history
  • Loading branch information
polydez authored and bobbinth committed May 13, 2024
1 parent 04aff75 commit 5f5ea97
Show file tree
Hide file tree
Showing 9 changed files with 469 additions and 197 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.3.0 (TBD)

* [BREAKING] Removed the transaction script root output from the transaction kernel (#608).
* [BREAKING] Refactored account update details, moved `Block` to `miden-objects` (#618, #621).

## 0.2.3 (2024-04-26) - `miden-tx` crate only

Expand All @@ -27,7 +28,8 @@
* Improved `ProvenTransaction` serialization (#543).
* Implemented note tree wrapper structs (#560).
* [BREAKING] Migrated to v0.9 version of Miden VM (#567).
* [BREAKING] Added account storage type parameter to `create_basic_wallet` and `create_basic_fungible_faucet` (miden-lib crate only) (#587).
* [BREAKING] Added account storage type parameter to `create_basic_wallet` and `create_basic_fungible_faucet` (miden-lib
crate only) (#587).
* Removed serialization of source locations from account code (#590).

## 0.1.1 (2024-03-07) - `miden-objects` crate only
Expand Down
13 changes: 6 additions & 7 deletions miden-tx/src/prover/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use miden_lib::transaction::{ToTransactionKernelInputs, TransactionKernel};
use miden_objects::{
accounts::delta::AccountUpdateDetails,
notes::Nullifier,
transaction::{
AccountDetails, InputNotes, ProvenTransaction, ProvenTransactionBuilder, TransactionWitness,
},
transaction::{InputNotes, ProvenTransaction, ProvenTransactionBuilder, TransactionWitness},
};
use miden_prover::prove;
pub use miden_prover::ProvingOptions;
Expand Down Expand Up @@ -75,18 +74,18 @@ impl TransactionProver {

let builder = match account_id.is_on_chain() {
true => {
let account_details = if tx_witness.account().is_new() {
let account_update_details = if tx_witness.account().is_new() {
let mut account = tx_witness.account().clone();
account
.apply_delta(&account_delta)
.map_err(TransactionProverError::InvalidAccountDelta)?;

AccountDetails::Full(account)
AccountUpdateDetails::New(account)
} else {
AccountDetails::Delta(account_delta)
AccountUpdateDetails::Delta(account_delta)
};

builder.account_details(account_details)
builder.account_update_details(account_update_details)
},
false => builder,
};
Expand Down
4 changes: 2 additions & 2 deletions miden-tx/src/verifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ impl TransactionVerifier {
// build stack inputs and outputs
let stack_inputs = TransactionKernel::build_input_stack(
transaction.account_id(),
transaction.initial_account_hash(),
transaction.account_update().init_state_hash(),
transaction.input_notes().commitment(),
transaction.block_ref(),
);
let stack_outputs = TransactionKernel::build_output_stack(
transaction.final_account_hash(),
transaction.account_update().final_state_hash(),
transaction.output_notes().commitment(),
);

Expand Down
56 changes: 55 additions & 1 deletion objects/src/accounts/delta/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use alloc::string::ToString;

use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Word, ZERO,
Account, ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable,
Word, ZERO,
};
use crate::{assets::Asset, AccountDeltaError};

Expand Down Expand Up @@ -83,6 +84,28 @@ impl AccountDelta {
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AccountUpdateDetails {
/// Account is private (no on-chain state change).
Private,

/// The whole state is needed for new accounts.
New(Account),

/// For existing accounts, only the delta is needed.
Delta(AccountDelta),
}

impl AccountUpdateDetails {
/// Returns `true` if the account update details are for private account.
pub fn is_private(&self) -> bool {
matches!(self, Self::Private)
}
}

// SERIALIZATION
// ================================================================================================

impl Serializable for AccountDelta {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.storage.write_into(target);
Expand All @@ -104,6 +127,37 @@ impl Deserializable for AccountDelta {
}
}

impl Serializable for AccountUpdateDetails {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
AccountUpdateDetails::Private => {
0_u8.write_into(target);
},
AccountUpdateDetails::New(account) => {
1_u8.write_into(target);
account.write_into(target);
},
AccountUpdateDetails::Delta(delta) => {
2_u8.write_into(target);
delta.write_into(target);
},
}
}
}

impl Deserializable for AccountUpdateDetails {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match u8::read_from(source)? {
0 => Ok(Self::Private),
1 => Ok(Self::New(Account::read_from(source)?)),
2 => Ok(Self::Delta(AccountDelta::read_from(source)?)),
v => Err(DeserializationError::InvalidValue(format!(
"Unknown variant {v} for AccountDetails"
))),
}
}
}

// HELPER FUNCTIONS
// ================================================================================================

Expand Down
191 changes: 190 additions & 1 deletion objects/src/block/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,195 @@
use alloc::vec::Vec;

use super::{Digest, Felt, Hasher, ZERO};

mod header;
pub use header::BlockHeader;
mod note_tree;
pub use note_tree::BlockNoteTree;
pub use note_tree::{BlockNoteIndex, BlockNoteTree};

use crate::{
accounts::{delta::AccountUpdateDetails, AccountId},
notes::Nullifier,
transaction::OutputNote,
utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
};

pub type NoteBatch = Vec<OutputNote>;

// BLOCK
// ================================================================================================

/// A block in the Miden chain.
///
/// A block contains information resulting from executing a set of transactions against the chain
/// state defined by the previous block. It consists of 3 main components:
/// - A set of change descriptors for all accounts updated in this block. For private accounts,
/// the block contains only the new account state hashes; for public accounts, the block also
/// contains a set of state deltas which can be applied to the previous account state to get the
/// new account state.
/// - A set of new notes created in this block. For private notes, the block contains only note IDs
/// and note metadata; for public notes, full note details are recorded.
/// - A set of new nullifiers created for all notes that were consumed in the block.
///
/// In addition to the above components, a block also contains a block header which contains
/// commitments to the new state of the chain as well as a ZK proof attesting that a set of valid
/// transactions was executed to transition the chain into the state described by this block (the
/// ZK proof part is not yet implemented).
#[derive(Debug, Clone)]
pub struct Block {
/// Block header.
header: BlockHeader,

/// Account updates for the block.
updated_accounts: Vec<BlockAccountUpdate>,

/// Note batches created in transactions in the block.
created_notes: Vec<NoteBatch>,

/// Nullifiers produced in transactions in the block.
created_nullifiers: Vec<Nullifier>,
//
// TODO: add zk proof
}

impl Block {
/// Returns a new [Block] instantiated from the provided components.
///
/// Note: consistency of the provided components is not validated.
pub const fn new(
header: BlockHeader,
updated_accounts: Vec<BlockAccountUpdate>,
created_notes: Vec<NoteBatch>,
created_nullifiers: Vec<Nullifier>,
) -> Self {
Self {
header,
updated_accounts,
created_notes,
created_nullifiers,
}
}

/// Returns the block header.
pub fn header(&self) -> BlockHeader {
self.header
}

/// Returns a set of account update descriptions for all accounts updated in this block.
pub fn updated_accounts(&self) -> &[BlockAccountUpdate] {
&self.updated_accounts
}

/// Returns a set of note batches containing all notes created in this block.
pub fn created_notes(&self) -> &[NoteBatch] {
&self.created_notes
}

/// Returns an iterator over all notes created in this block.
///
/// Each note is accompanies with a corresponding index specifying where the note is located
/// in the blocks note tree.
pub fn notes(&self) -> impl Iterator<Item = (BlockNoteIndex, &OutputNote)> {
self.created_notes.iter().enumerate().flat_map(|(batch_idx, notes)| {
notes.iter().enumerate().map(move |(note_idx_in_batch, note)| {
(BlockNoteIndex::new(batch_idx, note_idx_in_batch), note)
})
})
}

/// Returns a set of nullifiers for all notes consumed in the block.
pub fn created_nullifiers(&self) -> &[Nullifier] {
&self.created_nullifiers
}
}

impl Serializable for Block {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.header.write_into(target);
self.updated_accounts.write_into(target);
self.created_notes.write_into(target);
self.created_nullifiers.write_into(target);
}
}

impl Deserializable for Block {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
Ok(Self {
header: BlockHeader::read_from(source)?,
updated_accounts: <Vec<BlockAccountUpdate>>::read_from(source)?,
created_notes: <Vec<NoteBatch>>::read_from(source)?,
created_nullifiers: <Vec<Nullifier>>::read_from(source)?,
})
}
}

// BLOCK ACCOUNT UPDATE
// ================================================================================================

/// Describes the changes made to an account state resulting from executing transactions contained
/// in a block.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockAccountUpdate {
/// ID of the updated account.
account_id: AccountId,

/// Hash of the new state of the account.
new_state_hash: Digest,

/// A set of changes which can be applied to the previous account state (i.e., the state as of
/// the last block) to get the new account state. For private accounts, this is set to
/// [AccountUpdateDetails::Private].
details: AccountUpdateDetails,
}

impl BlockAccountUpdate {
/// Returns a new [BlockAccountUpdate] instantiated from the specified components.
pub const fn new(
account_id: AccountId,
new_state_hash: Digest,
details: AccountUpdateDetails,
) -> Self {
Self { account_id, new_state_hash, details }
}

/// Returns the ID of the updated account.
pub fn account_id(&self) -> AccountId {
self.account_id
}

/// Returns the hash of the new account state.
pub fn new_state_hash(&self) -> Digest {
self.new_state_hash
}

/// Returns the description of the updates for public accounts.
///
/// These descriptions can be used to build the new account state from the previous account
/// state.
pub fn details(&self) -> &AccountUpdateDetails {
&self.details
}

/// Returns `true` if the account update details are for private account.
pub fn is_private(&self) -> bool {
self.details.is_private()
}
}

impl Serializable for BlockAccountUpdate {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.account_id.write_into(target);
self.new_state_hash.write_into(target);
self.details.write_into(target);
}
}

impl Deserializable for BlockAccountUpdate {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
Ok(Self {
account_id: AccountId::read_from(source)?,
new_state_hash: Digest::read_from(source)?,
details: AccountUpdateDetails::read_from(source)?,
})
}
}
Loading

0 comments on commit 5f5ea97

Please sign in to comment.