-
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
[RFC] Example-driven proposal for Move-based programmability #4
Conversation
870cfb3
to
a13ed34
Compare
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.
Wow. Mind blowing stuff.
// logic for their structs that cannot be subverted by other modules | ||
public native fun transfer<T: key>(obj: T, recipient: Authenticator); | ||
|
||
/// Transfer ownership of `obj` to another object `id`. Afterward, `obj` |
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.
Nice one: also I think the constraint is a little stronger than it may be used only when another object is in the Tx. The 'owner' object smart contact needs to invoke the operation that uses the owned object?
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 don't think I understood this. Here's what I was thinking in more detail; let me know which part is too strong.
- Every transaction has to be signed by one end-user Bob
- A transaction can include objects directly owned by Bob (e.g.,
obj1.authenticator = Bob
) - Under the George proposal, objects can own other objects, and a the transaction can also include objects transitively owned by Bob (e.g.,
ParentObj.authenticator = GranparentObj, ChildObj.Authenticator=ParentObj, GrandparentObj.authenticator = Bob
) - If a transaction wants to use ChildObj, it must also include ParentObj and GrandparentObj + be signed by Bob. Otherwise, we can't verify which end-user's signature grants permission to include ChildObj.
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.
Thanks for these examples, they're great!
In several cases, I've noticed portions of examples that angle towards ACLs: we have a list of subjects with special permissions:
const ADMIN
inHero.move
,const OWNER
inTrustedCoin.move
I've indicated other "roles" that may arise, for instance on the EscrowSwap
contract.
Sidenote:
Just so we're on the same page re: vocabulary in the context of this discussion:
- we're dealing with resources (e.g. a boar, a sword, a
TreasuryCap
),- and we're dealing with subjects (e.g. the owner of the passed resources, the admin account, the coin
OWNER
account),ACLs are what guarantees safety when you "classify" what can happen depending on the resource at hand (e.g. for the
Hero
contract, the normal callers have ambient permissions toslay
,heal
, ..., whereas the admin also has permissions tocreate_{potion, boar}
). And, as you know, capabilities are what happen when you "sort" permissions on the subject at hand (e.g. Bob has aSword
, and aBoar
, so he cancreate
a Hero, and maybeslay
, but notheal
).
Overall, I would like some details on the management of the contract ACL list, if you will.
- if it is, as demonstrated, limited to constants —requiring a full-fleged contract update to change— do we plan to manage those updates more loosely in FastX? If not, is this manageable? How do we address the proliferation of contracts who want a "fork" of the
HeroesOfBC&Magic
game? - Will we materialize explicit ACLs? What would that look like? Maybe a
Permissions
type, pointers from smart contracts to a specific handful of coarsePermissions
instances, and a global namespace tyingPermissions
to users ? - How do we delegate and revoke permissions?
sword: Sword, | ||
} | ||
|
||
/// The hero's trusty sword |
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 later. What I haven't seen is the Sword creation though.
) { | ||
let v = read_field(to_read); | ||
write_field(to_write, v + int_input); | ||
transfer(to_consume, Authenticator::new(bytes_input)); |
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 transfer foo
(by, say a —hypothetical— mutation of an owner
field in foo), then any smart contract you'd give this &mut foo
to could either give your foo
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.
public fun create<T: key + store, ExchangeForT: key + store>( | ||
recipient: Authenticator, | ||
third_party: Authenticator, | ||
exchange_for: IDBytes, | ||
escrowed: T, | ||
ctx: &mut TxContext | ||
) { |
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.
IIUC, nothing prevents e.g. me from escrowing the object to myself, and then scamming others into participating in an exchange with me.
Without a return_to_sender
call automatically issued by a timekeeper I do not control (as discussed later), this allows me to block another person's object (as well as my own), trolling them, or alternatively, holding this as a perpetual American option.
In other terms, we need to make sure the 3d party is not only not the sender (symmetrically receiver), but also not otherwise controlled by the sender (resp. receiver).
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, interesting point. I don't think there's much we can do about this--at the very least, we can assert that sender
and recipient
are not the same as third_party
. But of course in general, third_party
can be recipient
with a different authenticator, and there's no good way to prevent that.
I think this attack is a special case of "if the trusted third party is not trustworthy, your objects will get locked up", but makes the important point that the trusted third party might have more motivation to lock up your object than you might think (i.e., trolling is not rational, holding a free American option is...)
If you want to use objects defined by HeroesOfBC&Magic in a different game, that should be possible by simply defining a new module that adds new logic around those objects. However, if the user wants to mutate the objects in ways that aren't allowed by the original module, or change the game logic, they'll need to fork the contract. If it's well-engineered, HeroesOfBC&Magic will be split across multiple modules that isolate the more generic bits from the game-specific ones + allows lots of customization of the generic bits.
TBD. I think there are interesting decisions between simplicity (it doesn't get any easier than asserting that the sender authenticator is equal to a constant) and more complex, but possibly harder-to-use access control functionality. My inclination is to start with the simplest methods, then add more as there are needs/clear value props (using capabilities, which are more complex but enable delegation is a great example).
See #4 (comment) |
ced417d
to
c0c0fa5
Compare
c0c0fa5
to
c1ed7cc
Compare
[Object Ownership #4] Prevent circular ownership
# This is the 1st commit message: reword # The commit message MystenLabs#2 will be skipped: # fix # The commit message MystenLabs#3 will be skipped: # fix # The commit message MystenLabs#4 will be skipped: # fix
See included README