-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate batch kernel parts from miden-node (#1112)
* feat: Add miden-batch-prover crate * feat: Add `BatchId` * Introduce `AccountUpdateError` * feat: Add `ProposedBatch` * feat: Add `LocalBatchProver` * feat: Add `ProvenBatch` * feat: Migrate `LocalBatchProver` from node * chore: Rename `NoteAuthenticationInfo` * feat:Add batch expiration block num * chore: Use core instead of std for `Display` * feat: Migrate `MockProvenTxBuilder` * feat: Test tx ordering in batches * feat: Add `BatchAccountUpdate` * chore: Extend test assertions * feat: Refactor and document batch output note tracker * feat: Add input/output notes commitment test * feat: Remove `BlockNumber::from_usize` * feat: Check for duplicate input notes * feat: Add unauthenticated/authenticated scenario tests * chore: Misc doc fixes * feat: Move `ProposedBatch` and `ProvenBatch` to objects * chore: Add changelog entry * feat: Use MockChain for batch tests (partially) * chore: Move most validation to `ProposedBatch` * feat: Use MockChain for all tests * feat: Implement note authentication and tests for it * feat: Verify transactions in batch * chore: Document error conditions on proposed batch * feat: Check max input/output notes and account updates in batch * feat: Check for duplicate transactions in batch * feat: Add tests with a circular note dependency * feat: Rename batch prover crate * chore: Address review comments * chore: Unbox the `BlockHeader` * chore: Remove unused dependencies * feat: Use `InputNotes` rather than `Vec` * feat: Compute batch ID as sequential hash over tx ID and account ID * chore: Remove `BlockInclusionProof` and `NoteInclusionProofs` * chore: Address more review comments * chore: Move tests into dedicated file * feat: Add `block_num` field to `ProvenTransaction` * feat: Add `ChainMmr::from_mmr` * feat: Add `BatchAccountUpdate::into_parts` * feat: Add `ProvenBatch::produced_nullifiers` * feat: Add `BatchId::compute_from_ids` * feat: Expose `BatchProveError` and rename `BatchProposeError` * feat: Remove `compute` prefix from batch id constructors * chore: Move `ChainMmr::from_mmr` behind test flag * chore: Address review comments * feat: Disallow empty transaction batches * chore: Address review comments
- Loading branch information
1 parent
723e47c
commit e82dee0
Showing
32 changed files
with
1,939 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
use alloc::vec::Vec; | ||
|
||
use vm_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; | ||
use vm_processor::{DeserializationError, Digest}; | ||
|
||
use crate::{ | ||
account::{delta::AccountUpdateDetails, AccountId}, | ||
errors::BatchAccountUpdateError, | ||
transaction::{ProvenTransaction, TransactionId}, | ||
}; | ||
|
||
// BATCH ACCOUNT UPDATE | ||
// ================================================================================================ | ||
|
||
/// Represents the changes made to an account resulting from executing a batch of transactions. | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub struct BatchAccountUpdate { | ||
/// ID of the updated account. | ||
account_id: AccountId, | ||
|
||
/// Commitment to the state of the account before this update is applied. | ||
/// | ||
/// Equal to `Digest::default()` for new accounts. | ||
initial_state_commitment: Digest, | ||
|
||
/// Commitment to the state of the account after this update is applied. | ||
final_state_commitment: Digest, | ||
|
||
/// IDs of all transactions that updated the account. | ||
transactions: Vec<TransactionId>, | ||
|
||
/// A set of changes which can be applied to the previous account state (i.e. `initial_state`) | ||
/// to get the new account state. For private accounts, this is set to | ||
/// [`AccountUpdateDetails::Private`]. | ||
details: AccountUpdateDetails, | ||
} | ||
|
||
impl BatchAccountUpdate { | ||
// CONSTRUCTORS | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
/// Creates a [`BatchAccountUpdate`] by cloning the update and other details from the provided | ||
/// [`ProvenTransaction`]. | ||
pub fn from_transaction(transaction: &ProvenTransaction) -> Self { | ||
Self { | ||
account_id: transaction.account_id(), | ||
initial_state_commitment: transaction.account_update().init_state_hash(), | ||
final_state_commitment: transaction.account_update().final_state_hash(), | ||
transactions: vec![transaction.id()], | ||
details: transaction.account_update().details().clone(), | ||
} | ||
} | ||
|
||
// PUBLIC ACCESSORS | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
/// Returns the ID of the updated account. | ||
pub fn account_id(&self) -> AccountId { | ||
self.account_id | ||
} | ||
|
||
/// Returns a commitment to the state of the account before this update is applied. | ||
/// | ||
/// This is equal to [`Digest::default()`] for new accounts. | ||
pub fn initial_state_commitment(&self) -> Digest { | ||
self.initial_state_commitment | ||
} | ||
|
||
/// Returns a commitment to the state of the account after this update is applied. | ||
pub fn final_state_commitment(&self) -> Digest { | ||
self.final_state_commitment | ||
} | ||
|
||
/// Returns a slice of [`TransactionId`]s that updated this account's state. | ||
pub fn transactions(&self) -> &[TransactionId] { | ||
&self.transactions | ||
} | ||
|
||
/// Returns the contained [`AccountUpdateDetails`]. | ||
/// | ||
/// This update 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 a private account. | ||
pub fn is_private(&self) -> bool { | ||
self.details.is_private() | ||
} | ||
|
||
// MUTATORS | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
/// Merges the transaction's update into this account update. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Returns an error if: | ||
/// - The account ID of the merging transaction does not match the account ID of the existing | ||
/// update. | ||
/// - The merging transaction's initial state commitment does not match the final state | ||
/// commitment of the current update. | ||
/// - If the underlying [`AccountUpdateDetails::merge`] fails. | ||
pub fn merge_proven_tx( | ||
&mut self, | ||
tx: &ProvenTransaction, | ||
) -> Result<(), BatchAccountUpdateError> { | ||
if self.account_id != tx.account_id() { | ||
return Err(BatchAccountUpdateError::AccountUpdateIdMismatch { | ||
transaction: tx.id(), | ||
expected_account_id: self.account_id, | ||
actual_account_id: tx.account_id(), | ||
}); | ||
} | ||
|
||
if self.final_state_commitment != tx.account_update().init_state_hash() { | ||
return Err(BatchAccountUpdateError::AccountUpdateInitialStateMismatch(tx.id())); | ||
} | ||
|
||
self.details = self.details.clone().merge(tx.account_update().details().clone()).map_err( | ||
|source_err| BatchAccountUpdateError::TransactionUpdateMergeError(tx.id(), source_err), | ||
)?; | ||
self.final_state_commitment = tx.account_update().final_state_hash(); | ||
self.transactions.push(tx.id()); | ||
|
||
Ok(()) | ||
} | ||
|
||
// CONVERSIONS | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
/// Consumes the update and returns the non-[`Copy`] parts. | ||
pub fn into_parts(self) -> (Vec<TransactionId>, AccountUpdateDetails) { | ||
(self.transactions, self.details) | ||
} | ||
} | ||
|
||
// SERIALIZATION | ||
// ================================================================================================ | ||
|
||
impl Serializable for BatchAccountUpdate { | ||
fn write_into<W: ByteWriter>(&self, target: &mut W) { | ||
self.account_id.write_into(target); | ||
self.initial_state_commitment.write_into(target); | ||
self.final_state_commitment.write_into(target); | ||
self.transactions.write_into(target); | ||
self.details.write_into(target); | ||
} | ||
} | ||
|
||
impl Deserializable for BatchAccountUpdate { | ||
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> { | ||
Ok(Self { | ||
account_id: AccountId::read_from(source)?, | ||
initial_state_commitment: Digest::read_from(source)?, | ||
final_state_commitment: Digest::read_from(source)?, | ||
transactions: <Vec<TransactionId>>::read_from(source)?, | ||
details: AccountUpdateDetails::read_from(source)?, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use alloc::{string::String, vec::Vec}; | ||
|
||
use vm_core::{Felt, ZERO}; | ||
use vm_processor::Digest; | ||
|
||
use crate::{ | ||
account::AccountId, | ||
transaction::{ProvenTransaction, TransactionId}, | ||
Hasher, | ||
}; | ||
|
||
// BATCH ID | ||
// ================================================================================================ | ||
|
||
/// Uniquely identifies a batch of transactions, i.e. both | ||
/// [`ProposedBatch`](crate::batch::ProposedBatch) and [`ProvenBatch`](crate::batch::ProvenBatch). | ||
/// | ||
/// This is a sequential hash of the tuple `(TRANSACTION_ID || [account_id_prefix, | ||
/// account_id_suffix, 0, 0])` of all transactions and the accounts their executed against in the | ||
/// batch. | ||
#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd)] | ||
pub struct BatchId(Digest); | ||
|
||
impl BatchId { | ||
/// Calculates a batch ID from the given set of transactions. | ||
pub fn from_transactions<'tx, T>(txs: T) -> Self | ||
where | ||
T: Iterator<Item = &'tx ProvenTransaction>, | ||
{ | ||
Self::from_ids(txs.map(|tx| (tx.id(), tx.account_id()))) | ||
} | ||
|
||
/// Calculates a batch ID from the given transaction ID and account ID tuple. | ||
pub fn from_ids(iter: impl Iterator<Item = (TransactionId, AccountId)>) -> Self { | ||
let mut elements: Vec<Felt> = Vec::new(); | ||
for (tx_id, account_id) in iter { | ||
elements.extend_from_slice(tx_id.as_elements()); | ||
let [account_id_prefix, account_id_suffix] = <[Felt; 2]>::from(account_id); | ||
elements.extend_from_slice(&[account_id_prefix, account_id_suffix, ZERO, ZERO]); | ||
} | ||
|
||
Self(Hasher::hash_elements(&elements)) | ||
} | ||
|
||
/// Returns the elements representation of this batch ID. | ||
pub fn as_elements(&self) -> &[Felt] { | ||
self.0.as_elements() | ||
} | ||
|
||
/// Returns the byte representation of this batch ID. | ||
pub fn as_bytes(&self) -> [u8; 32] { | ||
self.0.as_bytes() | ||
} | ||
|
||
/// Returns a big-endian, hex-encoded string. | ||
pub fn to_hex(&self) -> String { | ||
self.0.to_hex() | ||
} | ||
} | ||
|
||
impl core::fmt::Display for BatchId { | ||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
write!(f, "{}", self.to_hex()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,14 @@ | ||
mod note_tree; | ||
pub use note_tree::BatchNoteTree; | ||
|
||
mod batch_id; | ||
pub use batch_id::BatchId; | ||
|
||
mod account_update; | ||
pub use account_update::BatchAccountUpdate; | ||
|
||
mod proven_batch; | ||
pub use proven_batch::ProvenBatch; | ||
|
||
mod proposed_batch; | ||
pub use proposed_batch::ProposedBatch; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.