-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Allow external structs. #3045
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
Allow external structs. #3045
Conversation
Co-authored-by: Michael Benfield <mike.benfield@gmail.com>
|
Rebase and squash #2798 and opened a new PR with Michael as the co-author of the single commit. This will make it easier to keep rebasing as we review this. |
- Add a few more tests
71adc3d to
3e0e03c
Compare
|
Review comments addressed in 3e0e03c |
|
Please no more force pushing after reviews have started |
vicsn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one test seems to fail. If you run with REWRITE_EXPECTATIONS=1, please still check the new output makes sense.
| 2 => Ok(Self::Array(ArrayType::read_le(&mut reader)?)), | ||
| 3.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))), | ||
| 3 => Ok(Self::ExternalStruct(Locator::read_le(&mut reader)?)), | ||
| 4.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exists in current implementation, but it might be more clear to say
| 4.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))), | |
| 4.. => Err(error(format!("Failed to deserialize plaintext type variant {variant}"))), |
| use crate::{ArrayType, Identifier, LiteralType, Locator, ProgramID, StructType}; | ||
| use snarkvm_console_network::prelude::*; | ||
|
|
||
| /// A `PlaintextType` defines the type parameter for a literal, struct, or array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// A `PlaintextType` defines the type parameter for a literal, struct, or array. | |
| /// A `PlaintextType` defines the type parameter for a literal, struct, array, or external struct. |
| // Look up the struct | ||
| let struct_ = get_external_struct(locator)?; | ||
|
|
||
| // Account for the plaintext variant bits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could consider unifying with the struct logic in the variant above, for consistency.
Ditto for size_in_bits_raw.
synthesizer/process/src/stack/mod.rs
Outdated
| } | ||
| } | ||
|
|
||
| for mapping in self.program.mappings().values() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is performed in verify.rs gated by a consensus version. Does it also need to happen here?
|
|
||
| /// Returns whether this constructor refers to an external struct. | ||
| pub fn contains_external_struct(&self) -> bool { | ||
| self.commands.iter().any(|command| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check the other command variants?
| pub fn contains_external_struct(&self) -> bool { | ||
| self.commands | ||
| .iter() | ||
| .any(|command| matches!(command, Command::Instruction(inst) if inst.contains_external_struct())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check the other command variants?
| // Only cast instructions may contain an explicit reference to an external struct. | ||
| // Calls may produce them, but they don't explicitly reference the type, and that's | ||
| // always been allowed. | ||
| matches!(self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a new instruction is added, we may forget to update this code.
For example, dynamic dispatch would in theory support get.dynamic.record r0.microcredits into r1 as foo.aleo/data, but it's easy for us to forget to update this rule.
This also holds true for some of the other checks that match on the instructions?
Is there a more reliable way we can enforce these properties?
| // Cache the plaintext bits, and return the struct. | ||
| Ok(circuit::Plaintext::Struct(members, Default::default())) | ||
| } | ||
| PlaintextType::ExternalStruct(_identifier) => todo!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bailing with an error message is fine if this is not supported.
| // Cache the plaintext bits, and return the struct. | ||
| Ok(Plaintext::Struct(members, Default::default())) | ||
| } | ||
| PlaintextType::ExternalStruct(_identifier) => todo!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bailing with an error message is fine if this is not supported.
| Ok(stack) | ||
| } | ||
|
|
||
| // ---------- 1️⃣ Literal equivalence ---------- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. We should get rid of the numerical emojis in this file, to support our friends whose editors can't render :)
synthesizer/src/vm/tests/test_v13.rs
Outdated
| } | ||
|
|
||
| // This test verifies that a program with external structs can be deployed on | ||
| // consensus version 12. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // consensus version 12. | |
| // consensus version 13. |
synthesizer/src/vm/tests/test_v13.rs
Outdated
| let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng); | ||
|
|
||
| // Initialize the VM at the correct height. | ||
| let v10_height = CurrentNetwork::CONSENSUS_HEIGHT(consensus_version).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let v10_height = CurrentNetwork::CONSENSUS_HEIGHT(consensus_version).unwrap(); | |
| let height = CurrentNetwork::CONSENSUS_HEIGHT(consensus_version).unwrap(); |
synthesizer/src/vm/tests/test_v13.rs
Outdated
|
|
||
| // Initialize the VM at the correct height. | ||
| let v10_height = CurrentNetwork::CONSENSUS_HEIGHT(consensus_version).unwrap(); | ||
| let vm = crate::vm::test_helpers::sample_vm_at_height(v10_height, rng); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let vm = crate::vm::test_helpers::sample_vm_at_height(v10_height, rng); | |
| let vm = crate::vm::test_helpers::sample_vm_at_height(height, rng); |
synthesizer/src/vm/verify.rs
Outdated
| if consensus_version < ConsensusVersion::V13 { | ||
| ensure!( | ||
| !deployment.program().contains_external_struct(), | ||
| "Invalid deployment transaction '{id}' - external structs may only be used beginning with Consensus version 10" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| "Invalid deployment transaction '{id}' - external structs may only be used beginning with Consensus version 10" | |
| "Invalid deployment transaction '{id}' - external structs may only be used beginning with `ConsensusVersion::V13`" |
|
|
||
| // Build two identical committee_state structs | ||
| cast r0 r1 into r2 as credits.aleo/committee_state; | ||
| cast r0 r1 into r3 as credits.aleo/committee_state; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it would be worthwhile to test two arrays one with external and one with local?
d0cd
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's getting close, I'm aligned with this notion of partial nominal and structural equivalence. Left a few suggestions.
A couple points/questions:
- What is the import rule? If a program A uses an external struct in B which itself uses an external struct in C, does A have to import B and C? It would be great to document and test the rule.
- Before merging, we should refresh the program regressions and run it to make sure that the VM can still be initialized with all of the new checks.
- Fix a bug related to type equivalence for nested external structs
|
@d0cd I believe I addressed all your comments in 93e4e48 except for
I'm not sure why the check is gated by a consensus version check in
I added a test that showcases that transitive dependencies are not required at the top level as long as the top level does not explicitly use the external struct name directly. I think this rule matches how
What does this mean exactly? |
The consensus version check in verify.rs was added to ensure all validators agree external structs are only enabled from the new consensus version onwards. I think this comment's point is that the logic in synthesizer/process/src/stack/mod.rs might be duplicate and superfluous. |
The regression repo is here. |
…ich is where we test consensus stuff
|
Thank you everyone :) |
Specifically,
Introduce a PlaintextType::ExternalStruct, and allow referring to such a type in .aleo code by a Locator.
Allow casting to external struct types.
Let two struct types be considered the same type as long as they are structurally the same and have the same local names. This funky ruleset is effectively here for backwards compatibility. In more detail:
Also note this other usecase from a user: