Chore: update miden-protocol#2095
Conversation
13a8bfe to
4ce77a3
Compare
4ce77a3 to
fbd4960
Compare
igamigo
left a comment
There was a problem hiding this comment.
Mostly looks good, leaving some comments
| AccountUpdateDetails::Delta(update) => { | ||
| // Partial updates carry no storage we can inspect here. Forward them as updates and | ||
| // let the coordinator's actor registry filter to known network accounts. | ||
| Some(NetworkAccountEffect::Updated(update.clone())) | ||
| }, |
There was a problem hiding this comment.
Seems like, from an account delta, we can no longer know whether it belongs to a network account. Is this right @PhilippGackstatter? Then this will likely need some refactoring on the NTX builder side because I think we expect to have the account in the builder if we receive a delta. But I think some of this was being refactored anyway (cc @Mirko-von-Leipzig @SantiagoPittella)
There was a problem hiding this comment.
Hmm, the block subscription approach will use partial accounts
There was a problem hiding this comment.
Seems like, from an account delta, we can no longer know whether it belongs to a network account. Is this right @PhilippGackstatter?
Yes, sounds correct. Previously you could inspect the type on the ID, now you'd need to inspect storage, but the delta doesn't include full storage. So yes, you'd need to decide this at a different point.
There was a problem hiding this comment.
Returning to the specific situation of the ntx builder, we can check in our internal storage if the account is a network account, considering that at this point it should already exist in the database because when it was a complete account (the first occurrence) we should have stored it.
| let response = self | ||
| .store | ||
| .clone() | ||
| .are_network_accounts(tonic::Request::new(proto::account::AccountIdList { | ||
| account_ids: vec![tx.account_id().into()], | ||
| })) | ||
| .await | ||
| .map_err(|err| { | ||
| Status::internal(format!("network-account classification failed: {err}")) | ||
| })?; |
There was a problem hiding this comment.
We probably want to cache new account IDs that belong to network accounts in memory as a future enhancement here
| .map_err(DatabaseError::from) | ||
| } | ||
|
|
||
| /// Returns the subset of `account_ids` whose latest committed state is a network account. |
There was a problem hiding this comment.
I think for now we can assume that network accounts will either be network accounts forever or not (because right now changing account code and adding/removing components is not supported).
# Conflicts: # crates/rpc/src/tests.rs
There was a problem hiding this comment.
Mostly looks good; I'd like to merge this ASAP but I'm concerned by our Felt::new_unchecked and Word::unchecked usage. Especially when combined with Rng.
This could be addressed in a follow-up though.
Also want to mention that the network account stuff in the ntx context no longer matters - we're taking a different approach where we sync by block instead so most of this will fall away.
| let mut rng = ChaCha20Rng::from_seed(rand::random()); | ||
| let bridge_seed: [u64; 4] = rng.random(); | ||
| let bridge_seed = Word::from(bridge_seed.map(Felt::new)); | ||
| let bridge_seed = Word::from(bridge_seed.map(Felt::new_unchecked)); |
There was a problem hiding this comment.
This seems.. unsafe? Does word not implement Rng.
There was a problem hiding this comment.
I think this is fine, since you'd just want some seed. We use the same pattern for initializing account seeds in protocol.
But, yeah, it seems like a good idea to implement random sampling for words or use a RandomCoin here, though it also needs to be seeded with a random word...
There was a problem hiding this comment.
But then what is unchecked about it, if the constructor is valid? I assumed it would be invalid for some bit sequences that exceed the field modulus
There was a problem hiding this comment.
Opened 0xMiden/crypto#1025 in crypto to make this easier.
There was a problem hiding this comment.
But then what is
uncheckedabout it, if the constructor is valid?
It's to make callers aware that it effectively truncates u64s that exceed the field modulus.
| // The actor registry only contains accounts the builder has already classified as | ||
| // network. Wrap the id unconditionally and let the registry lookup filter for us; | ||
| // unknown accounts simply won't match. | ||
| let network_account_id = NetworkAccountId::new_trusted(delta.id()); |
There was a problem hiding this comment.
I would call it new_unchecked to align with our other methods.
|
@kkovaacs might be good to TAL at the rocksdb stuff? |
PhilippGackstatter
left a comment
There was a problem hiding this comment.
Looks good to me, overall.
Left a few comments, most are optional and noted for future improvements.
The only important one is the note ID / note details commitment switch up. In general, I think it would be worth double-checking that the changes from 0xMiden/protocol#2861 were correctly integrated in crates/store/src/db/* as there are a few note_commitment: NoteId types of occurrences left that will probably be confusing.
I did not review the rocksdb changes as I'm lacking context.
| pub fn network_account_id(&self) -> NetworkAccountId { | ||
| // SAFETY: This is a network account by construction. | ||
| self.protocol_account_id().try_into().unwrap() | ||
| // Trusted: constructors only produce this enum for accounts already classified as network | ||
| // (via the allowlist check above) or for updates that the caller filters through the actor | ||
| // registry. | ||
| NetworkAccountId::new_trusted(self.protocol_account_id()) |
There was a problem hiding this comment.
If it makes things easier, we could add the NetworkAccountId wrapper in miden-standards and return it from NetworkAccount::id.
| // lets us reconstruct the Account and look for the standardized | ||
| // `NetworkAccountNoteAllowlist` slot in storage; that is the new protocol-level | ||
| // definition of a network account. | ||
| if let Some(AccountUpdateDetails::Delta(delta)) = account_delta |
There was a problem hiding this comment.
Nit: I think comments shouldn't mention "changes", i.e. the comment will be a bit weird to read in the future.
| .iter() | ||
| .cloned() | ||
| .filter_map(|note| note.header().map(NoteHeader::to_commitment)) | ||
| .filter_map(|note| note.header().map(|h| h.id().as_word())) |
There was a problem hiding this comment.
Not necessary for this PR, but to increase type safety, would be useful to change get_block_inputs and get_batch_inputs to accept
unauthenticated_notes: impl Iterator<Item = NoteId> + Send,
instead of the current Word. And also to rename note_commitments to note_ids (for get_batch_inputs).
| pub fn output_note_commitments(&self) -> impl Iterator<Item = Word> + '_ { | ||
| self.inner | ||
| .output_notes() | ||
| .iter() | ||
| .map(miden_protocol::transaction::OutputNote::to_commitment) | ||
| self.inner.output_notes().iter().map(|n| n.id().as_word()) | ||
| } |
There was a problem hiding this comment.
Would be worth renaming to output_note_ids now.
| for raw in &raw_transactions { | ||
| let notes: Vec<NoteHeader> = Deserializable::read_from_bytes(&raw.output_notes)?; | ||
| all_note_commitments.extend(notes.iter().map(NoteHeader::to_commitment)); | ||
| all_note_commitments.extend(notes.iter().map(|h| h.id().as_word())); |
There was a problem hiding this comment.
| all_note_commitments.extend(notes.iter().map(|h| h.id().as_word())); | |
| all_note_ids.extend(notes.iter().map(NoteHeader::id)); |
Nit: Ideally this could be written like this.
| let mut output_notes_by_id = std::collections::BTreeMap::new(); | ||
| for chunk in all_note_commitments.chunks(QueryParamNoteCommitmentLimit::LIMIT) { | ||
| output_notes_by_id.extend(select_note_sync_records(conn, chunk)?); | ||
| } |
There was a problem hiding this comment.
Might be worth double-checking that note IDs and note commitments are used correctly here now. Looks like select_note_sync_records should now take NoteId as input.
Looking at the unchanged NoteSyncRecord with pub note_id: Word, was/is this correct? If this truly was note ID, why wasn't it NoteId and if it is now, would change it to NoteId.
| .filter_map(|note| { | ||
| let key = NoteId::from_raw(note.details_commitment().as_word()); | ||
| output_notes_by_id.get(&key).cloned() | ||
| }) |
There was a problem hiding this comment.
This converts a NoteDetailsCommitment into a NoteId which is not a valid conversion. Probably should be:
| .filter_map(|note| { | |
| let key = NoteId::from_raw(note.details_commitment().as_word()); | |
| output_notes_by_id.get(&key).cloned() | |
| }) | |
| .filter_map(|note| { | |
| let key = note.id(); | |
| output_notes_by_id.get(&key).cloned() | |
| }) |
| /// A publicly stored account, lives on-chain. | ||
| #[serde(alias = "public")] | ||
| #[default] | ||
| Public, | ||
| /// A private account, which must be known by interactors. | ||
| #[serde(alias = "private")] | ||
| Private, | ||
| } |
There was a problem hiding this comment.
Not sure if intentional, so just mentioning that the protocol treats Private as the default.
| note_index, | ||
| note_id: note.id().as_word(), | ||
| note_commitment: note.to_commitment(), | ||
| note_commitment: note.id().as_word(), |
There was a problem hiding this comment.
| note_commitment: note.id().as_word(), | |
| note_id: note.id().as_word(), |
There was a problem hiding this comment.
Renamed the DB columns to match the new API from 0xMiden/protocol#2861:
notes.note_id→notes.details_commitmentnotes.note_commitment→notes.note_id
This should make it clearer to understand after the protocol changes. a14e2b1
| if let Some(AccountUpdateDetails::Delta(delta)) = account_delta | ||
| && delta.is_full_state() | ||
| && let Ok(account) = Account::try_from(delta) | ||
| && let Ok(network_account) = NetworkAccount::new(account) |
There was a problem hiding this comment.
Are we checking the storage slot inside NetworkAccount::new?
d2aeeab to
5ee1c09
Compare
There was a problem hiding this comment.
Here we are going to panic with the current version. Since the Updated variant is now returned without filtering by network account. A way to handle this is to check if the get_account returned None then we can consider the account as non-network
|
heads up, as per d5fcf38 this PR no longer sets git's |
No description provided.