Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
[RFC] Example-driven proposal for Move-based programmability #4
[RFC] Example-driven proposal for Move-based programmability #4
Changes from all commits
c1ed7cc
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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.
Right now the input(.) function on a transaction should return all objects that are mutable and read-only. I wonder if there is value in returning a flag if we can detect that a mutable object is only read to help with execution scheduling? Unclear.
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'm not sure I understand the idea here--is it:
&
reference in the Movemain
&mut
reference in the Movemain
, but is never readI think we can potentially do something about each of these if desired.
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 guess what I am saying is that for the moment I am not assuming the executor knows the difference between & and &mut inputs. Maybe it should.
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, I see. I don't think it needs to know anything about this beyond the
read_only
flag for objects. But perhaps knowing more could help.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.
Rust definitely implements this in clippy warnings, under the
unnecessary_mut_passed
warning.One issue with silently doing the "upcast" to
&
is it makes it harder to reason about the contract from source, and that ability will be important for smart contract reviewers.What I guess I'm saying is: perhaps we should deal with this in the earlier phases of the UX (through tooling or early interpreter checks doing source validation), and make sure that by the time the user gets an
&mut
in, we're pretty darn sure it cannot be an&
?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.
Is this the id as it will be known in fastX, or simply a sequence number for the output that will be used to derive the object ID along with the Txdigest? My question is I supose: are the Ids known to the move code and need to be kept the same, or can we change the object_id as part of the runtime? Because the runtime instead of checking the id is correct can simply re-derive it in a safe way.
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.
Very good question!
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 agree with the pros but what about supporting transactions in non-move (eg native rust). I think we can keep the id logic in sync.
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.
Fully on board with supporting transactions in native Rust. My mental model is (and curious to know if it matches yours):
I'm a bit concerned about how we will pull off the last point, but it will become clearer once we understand the native operations + Move programming model.
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.
Just to check my understanding: by declare you mean here the caller has to pass a reference (which happens to be in the form (object_id, seq)) to the inputs, and mutation happens by passing a &mut reference. I think this is fine.
Note here we are not actually doing parallel execution, so our needs are a little more loose (although we can chose to be more strict to make parallel exec easier):
What I am trying to say is that what we need here is a little different from the write set prediction, and should be easier to get as it can be dynamic. Big win of course if it is cheap and static.
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.
Great thoughts, a ton to discuss here.
t;dlr: my primary motivation for asking the user to specify the input objects statically is actually better UX (though enabling more efficient execution). This may sound counterintuitive (asking to explicitly specify inputs sounds like terrible UX!), but hear me out.
main
where you only get asigner
+ some primitive params as input (like normal Move). Then, you can dynamically read/write/transfer whatever objects you want, as long as they belong to the inputsigner
.main
here that takes objects as input and then does stuff to them. The code for (1) is not very nice to write (especially in Move), and the user already had to do the nasty step of specifying the ID's, so we might as well let the runtime do (1) for them--UX win.main(o: &mut Obj1)
, their wallet can look for values of typeObj1
owned by the user, show them to the user, and prompt the user to select the appropriate one (without ever showing the user the ID or asking them to type it). If the user only has one object of that type, no need to ask them anything at all. I think this sort of thing is much easier to do offline with the benefit of DB queries, interactive feedback, etc instead of trying to do it in Move-land.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 do not think those things are easy to engineer at all, and in particular, I shudder at the thought of making sure what is reported to the user (from outside the chain) is authentic (&, to a minor degree, fresh). The potential for abuse in pointing the user at the wrong external object is worrisome.
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 hear these concerns, though am much more optimistic about how hard it is to build this (but let me know if/where I'm being naive):
If our wallet software can't do authenticated reads, I agree that we're in a lot of trouble...
I think the fastX model helps us a lot here. No one else can touch the objects that the user owns, so if a user has an authenticated read of an object and hasn't send a transaction to spend it, the user can be fully confident about its value/owner.
In addition, the only alternatives to better wallets/tooling I can think are:
At the end of the day, someone has to provide the ground truth for what object(s) a transactions will act on. Wallet software assisted by user feedback (when needed) is the only answer I can think of.
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.
Yes, but that only applies to himself. Any object owned by others could have been sent away unbeknownst to our user.
You're focused on how nobody can send away the things I've received. That's the easy part. The hard part is : how can I be sure I've received anything without trying to send or move it (or create a send that depends on it) ?
And that's the core of the issue: on a normal (consensus-driven) blockchain, you can have a block height and a converged view of the whole world at that particular block height. That, plus an estimate of how fresh the view is (which is directly comparable to the latest block height) is enough to have reasonable heuristics on what's going on. You can know what you own without a TX. You can know what others own. You know, for instance, if you're "too late behind" (as a Full node, you should sync before deciding on anything) and you can quantify (based on block frequency) how risky it is to postulate things about the world.
In FastNFT:
Don't get me wrong, I think there are ways around this, that you can construct indexations of the state of the blockchain, and get to notions of "partial view", "partial indexation for a fragment of things", and "speed of update of a partial view", I just want to underscore that this is a significant design and engineering challenge.
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 just love how you use the rust types to indicate read, write and delete.
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 surprised me: I was expecting to just drop the object here (delete) but instead you transfer it. Now I understand from a pure type theory why you need to pass it by val -- transfer takes a value. But why is transfer not taking a &mut? To prevent the called from mutating the object after the transfer I suppose?
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 think the idea here I think is to represent object ownership (in the pecuniary sense) by object ownership (in the Rust capability sense).
Even If a
&mut foo
sufficed at runtime to transferfoo
(by, say a —hypothetical— mutation of anowner
field in foo), then any smart contract you'd give this&mut foo
to could either give yourfoo
a new sword, or transfer it away, at its discretion.One language trick to reconcile your expectation of
drop
and your read of "taking a value" is to call it "consuming" a value.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.
Basically, what Francois said. But to add a few things:
This proposed programming model (and Move's more broadly) is that
&mut T
conveys the authority to mutate the contents ofT
, whereasT
conveys the authority to both mutate the contents ofT
and transfer it to another user (to "move" it, if you will :) ). This is a useful distinction to be able to express--e.g., you might want to give someone else permission to write to a field of an object you own, but not to take it away from you.Yes, this is also a concern. For example: imagine Alice transfers a
&mut Coin
to Bob and emits an event that records a transfer of 10 coins, but then (in the remainder of Alice's code) she usesCoin::split
on the mutable reference she still has to siphon off some of the value. Bob will see the event and expect to see 10 coins in his account, but he will have less.