-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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
[move] Added support for passing vectors of objects to entry functions #4349
Conversation
@@ -49,7 +49,8 @@ pub enum CallArg { | |||
Pure(Vec<u8>), | |||
// an object | |||
Object(ObjectArg), | |||
// TODO support more than one object (object vector of some sort) | |||
// a vector of objects | |||
ObjVec(Vec<ObjectArg>), |
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.
From an expressivity standpoint, I think what we really want here is Vec<CallArg>
--folks have also asked to pass in vectors of addresses, integers, strings, and object ID's. Is there a reason not to opt for this more generic approach?
I understand that this schema can also represent bad heterogenous vectors (e.g., a vector containing a mix of object args and ints), but I think that will be caught in a later phase.
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.
folks have also asked to pass in vectors of addresses, integers, and object ID's. Is there a reason not to opt for this more generic approach?
I did not put that much thought into supporting other types of vectors (yet), but since vectors of integers are already supported actually (I added a test for that in this PR), I thought that perhaps other vectors could be supported the same way, that it is via the Pure
variant. The schema using Vec<Box<CallArg>>
(I don't think Vec<CallArg>
would work) also implies nested vectors, which we probably won't support. In summary, I thought of this PR as exclusively attacking the vector-of-objects problem and doing a necessary refactoring when working on PRs for other types of vectors. as I wasn't 100% sure which way it would go.
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.
Makes sense--I didn't realize pure vectors were already supported!
I do think some kinds nested vectors (e.g., vector<vector<u8>>
are important to represent (e.g.) the public keys in a multisig. But arbitrary nested vectors are probably a bit much.
crates/sui-types/src/messages.rs
Outdated
// ObjVec is guaranteed to never contain shared objects | ||
debug_assert!(false); |
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.
Why do we have this restriction?
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.
I assumed that this PR is only meant to provide support for vectors of owned objects. I was not sure if there are any additional implications of supporting shared objects. On the surface there are not, but shared objects support is a bit newer (and a bit more complicated) so I did not want to make too many assumptions, so it would be good to get some advice on that.
Some((*id, state_view.read_object(id)?)) | ||
Some(vec![(*id, state_view.read_object(id)?)]) | ||
} | ||
CallArg::ObjVec(vec) => { |
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 is not an issue we need to solve now, but we're specializing the CallArg
types here and I fear we might need to do it in future for other types.
Not sure but is it even possible to create a general container type that prevents over-specialization of the adapter and sui types?
So we can decouple the inner Move type impl from sui types.
If this is not a real concern, please disregard.
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.
I share your concerns, but I am failing to think of anything that much more general.
I have calmed myself with the following line of reasoning:
- We can safely add new variants to this if needed, it will not be a breaking change (just will need to be epoch gated or gated by some similar mechanism)
- With that if we have something new in the future, like a map or something, we can do that
- Or similarly, if we come up with the supreme general approach, we can add that later while still supporting this special case (we did that with Move args in Diem fwiw. As we did not originally support general BCS arguments, only a few specific hand rolled cases)
- We can always interpret this differently in the adapter. Much like
CallArg::Object
can be used to populate&Obj
,&mut Obj
andObj
, we can have more than one meaning forObjVec
With this it feels fine enough to just be specific for vectors right now.
I am not going to take care of this in the current PR. If you can, please create the issue (I can look into it to see if I can do it myself, particularly if I get some pointers :-)). I would do it myself but I am traveling for the next two days. |
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.
I will need to take a closer look at the adapter code, just wanted to leave some comments. I should be able to take a look at this first thing in the morning!
if vec.is_empty() { | ||
return None; | ||
} |
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.
Fine here, but should we ban this in the transaction input checker?
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 should be OK to pass an empty vector (of internal object type) to an entry function (I even have a test for this) so I am not sure we should ban it.
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.
Ah sure, I could see it being useful
crates/sui-core/src/unit_tests/data/entry_point_vector/sources/objects_vector.move
Outdated
Show resolved
Hide resolved
return Ok(()); | ||
} | ||
if is_object_vector(view, function_type_args, param)? { |
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.
Took me a second staring at this. Maybe just if is_object(view, function_type_args, param)? || is_object_vector(view, function_type_args, param)?
?
I generally really like the if-return style of guards. Just confused me as its immediately followed by an if that "returns"
@@ -445,6 +447,323 @@ async fn test_object_owning_another_object() { | |||
assert_eq!(effects.deleted.len(), 2); | |||
} | |||
|
|||
#[tokio::test] | |||
async fn test_entry_point_vector() { |
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.
We also have the sui-adapter-transactional-tests
That might be easier for these, but maybe it's easier to do it programmatically in Rust?
Or is there some other reason you chose for these to be in Rust?
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.
Didn't know how to use vectors there (as we discussed elsewhere) but now that I do, I will add at least some tests there.
matches!(effects.status, ExecutionStatus::Failure { .. }), | ||
"{:?}", | ||
effects.status |
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 would be a good one for sui-adapter-transactional-tests, as we would get to see the error here.
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.
Will add!
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.
OK, I tried adding the tests but trying the vector[object(106)
syntax resulted in:
thread '<unnamed>' panicked at 'nested sui objects are not yet supported in args', crates/sui-transactional-test-runner/src/args.rs:92:36
If this is a transactional testing framework (temporary) limitation, my suggestion is to stick with integration tests for now and add the transactional ones (again, it should be quick) once the remaining testing framework kinks are ironed out.
// write length of the vector as uleb128 as it is encoded in BCS and then append | ||
// all (already serialized) object content data | ||
let mut res = vec![]; | ||
leb128::write::unsigned(&mut res, vec.len() as u64).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.
bit funky, but seems fine for now :)
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.
bit funky, but seems fine for now :)
It is a little, isn't it? :-) Could not think of a better/simpler way, though...
crates/sui-adapter/src/adapter.rs
Outdated
element_type = Some(arg_type); | ||
inner_vec_type = Some(param_type); |
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.
maybe a debug assert that if there was a previous value there, it should be the same?
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 reminded me that I wanted to add corresponding tests but didn't... Also, this should be handled by more than an assertion I think. In fact, we should be checking if each element's type is consistent with vector's signature and report and error (rather than panic) if it's not.
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.
We should look into adding transactional test support for vectors of objects for some more tests, but I think this should be good to go for now. Thanks!
if vec.is_empty() { | ||
return None; | ||
} |
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.
Ah sure, I could see it being useful
&mut by_value_objects, | ||
&mut object_type_map, | ||
)?; | ||
type_check_struct(view, type_args, idx, arg_type, param_type)?; |
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 PR implements passing vectors of objects to owned functions and resolves #492. One somewhat tricky part to handle was the fact that the adapter was taking advantage of the fact that one object was corresponding to one parameter - checking of various properties of both objects (based on object ID) and parameters (based on their index IDX) was happening in the same spots. This PR is attempting to refactor and re-use this code but it may result in some (minor) computational redundancy. Another somewhat tricky part was how to combine existing objects in to bcs-encoded vector that can be used as an argument - I am not sure if there is a better way than what this PR does, that is hand-encoding vector length and appending all objects to the result.