-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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
[fastx distsys] Nested objects, provenance, and replay prevention #98
Comments
I have a strong prejudice towards also maintaining the sequence number (or increasing it). One reason is that if we store any structure by (obj_id, seq, tx_digest) keys in a database we can efficiently tell what refers to the 'first' and what to the 'last' version of an object. Whereas if we reuse the seq then a reader may have to traverse the full hash chain of certs / objects to understand what they are missing. |
I agree with the importance of this use-case + think that the sequence number does not provide much value if we can't use it for this (especially if it's not needed for replay protection). I have two proposals for maintaining sequence numbers across wrapping, which I'll write in subsequent comments: |
Notes about this approach:
|
Notes about this approach:
|
I'm torn on these. (1) is definitely simpler/more direct, but it leaks more implementation details of FastX into Move, which will make it harder to reuse Move code from elsewhere in FastX (and vice versa). But maybe we can minimize the UX impact of this by requiring the first field to be (2) is definitely a bit more complex--in particular, the logic to create a |
On (2), an alternative is to require calling a native function (e.g. |
Hmm this may not be enforceable. One can wrap an object by writing into a reference, which cannot be detected by a verifier. That leaves another alternative which is when defining an object struct, if it embeds another object, the embedding needs to go through a wrapper instead of direct embedding. |
Trying approach (1) to addressing #98. In particular, this: - Extends the Move `ID` type to include a `version`. This allows all Move objects to carry their verson, and thus persist it across wrapping and unwrapping. But having it live inside `ID` saves us from having to rewrite the `ID`-related bytecode verifier passes or ask the programmer to write `struct S has key { id: ID, version: Version, ... }` to declare a FastX object. In fact, there are no changes from the programmer's perspective except that they can now read an object's version inside Move if they wish to. - Removed `version` from the Rust object types, since it now lives in the Move-managed `contents` field. Added a variety of helper functions for reading and writing the `version from Rust. - Changed the adapter to understand the new location of `version`. This actually simplifies the adapter code quite a bit. - Added a test that demonstrates that sequence numbers are maintained correctly across wrapping and unwrapping. There is one change introduced here that is important enough to mention separately *object versions now begin at 1*. That is, if a tranaction creates and then transfers an object `X`, the version of `X` in the transaction effects will be 1, not 0. The reasons why this change is needed are somewhat subtle. - This change happens because the adapter increments the sequence number of every transferred object. - You could imagine asking the adapter to instead check whether a transferred object was created by the current transaction, passed as an input, or unwrapped + only incrementing the sequence number in the second two cases. However, if sequence numbers of freshly created objects started at 0, the adapter would actually not be able to tell the difference between a freshly created object `O1` (will have seq 0) and an object `O2` that was created (will have seq 0), then subsquently wrapped (will still have seq 0), and later unwrapped (will still have seq 0!). - Another solution to this problem would be incrementing the sequence number of all objects passed as inputs to the transaction before beginning execution. This would allow freshly created objects to start at seq 0, but it would be slightly odd in that the programmer would pass in an object with seq `S`, but would see `S+1 if they try to read the sequence number from Move during the transaction. It seems most intuitive to maintain the invariant that the object you put into the transaction is exactly what you will read inside the transaction. In addition, that would require us to do the "created or transferred/unwrapped" special-casing described above, which makes the adapter a bit more complicated.
This goes deeper into an issue with object wrapping initially raised in https://docs.google.com/presentation/d/1-xpsmJC3VEaRPH9U1Lklap858MaU_E3YQsRcaiOYI0Y/edit?usp=sharing, slide 16.
Note that this is going off of the Overleaf https://www.overleaf.com/project/6187be7580dc35362b091a73, not what is currently implemented (since the current implementation is lagging behind the spec a bit). In particular, the discussion will not mention object sequence numbers/versions, since they are an optional feature that is useful for making sync more sane, but we could just as easily choose to omit them.
Let's say the following things happen:
T
creates an objectO
with idid1
. The derivedObjRef
is(id, digest(T), com(O))
. The authority updatesLockMap[(id, digest(T), com(O))] := T
andObjMap[id] := O
T'
takes this object as input and wraps it in a new object we will callW
. The authority updatesLockMap[(id, digest(T), com(O))] = \bot
andObjMap[id] := \bot
, as well as doing someW
-related updates that we will omit.T''
destroysW
, unwrapsO
from it, and sends it to an address. The authority updatesLockMap[(id, digest(T''), com(O))] := T
andObjMap[id] := O
This scheme achieves the following:
TxDigest
andid
combination in theLockMap
domain)O
retains the same ID even as it flows in and out of other objects)Discussion
However, there is some funkiness here:
O
forever, or wrapped it in another object. We could choose to push this problem to application devs (e.g., say "well-written applications should emit an event likeWrapped(W.id, O.id)
"), or choose to help more directly (e.g., we could doObjMap[id] := Wrapped(digest(T))
in step 2 instead, whereWrapped
is some special marker pointing to the tx that wrapped the object)ObjMap[id]
is set to\bot
, no subsequent assignments toObjMap[id]
can occur.The text was updated successfully, but these errors were encountered: