The aim of this post is to explicate a bit of my current expectation for how AVM → transaction function compilation should work (in the context of overall AVM → RM compilation). I don’t have an exact model worked out, just a high-level pattern which I think makes sense (open to counterarguments).
Let’s take as our basic frame the compilation of AVM programs to writes (as in, when we want to make state changes somewhere in the database). State changes are ultimately represented as transactions, which transaction functions can be used to generate. The read side of the question (a.k.a. indexing) is also important, and yet unaddressed, but is not the topic of this post.
So, somehow, we should be able to take an AVM program (perhaps in the context of some application definitions) and compile it to transaction function(s) and resource logic(s). How exactly? Let’s think about a simple program – starting with a message sent to O1, which then calls O2, which in turn calls O3, and then the return path back:
Assuming that “O2” and “O3” are, in some fashion, object references, this program involves lookup, when O1 looks up which version of O2 (represented as a resource) to call (consume), and O2 looks up which version of O3 (represented as a resource) to call (consume). We then face the question of where, in our distributed system, this lookup should happen. This is a choice which is independent of the high-level application semantics. Suppose that we compile this program to a transaction function which performs the lookups (and then returns a transaction with the appropriate state changes). This transaction function could be run locally (performing the lookups through scry calls with reference to the latest known state of the controller of the objects in question), or it could be run in post-ordering execution on the controller itself. Running the transaction function on the controller post-ordering guarantees that the lookup results will be current (i.e. no conflicts), but may be more expensive, and doesn’t guarantee the exact results of program execution, which the user may wish to do. Running the transaction function locally is likely cheaper (since the controller need only verify the transaction) and guarantees the exact results (since the transaction will revert and no state changes take place if the objects have changed in the meantime), but also means that there could be a conflict if the objects in question are changed between the time when the user runs the transaction function locally and the time when the controller validates the transaction.
We actually have a more granular choice: when we “start running” a program somewhere in the distributed system, for every lookup we encounter, we can decide whether to perform that lookup locally or encode it in a transaction function to be sent onwards. In a diagram, this might look something like:
In this example, partial execution takes place in three places: on node A, which finalizes the changes to object O2, node B, which finalizes the changes to object O3, and the controller, which finalizes the changes to objects O4 and O1 (and executes the transaction enacting the actual state changes, assuming that there are no conflicts). At each step, the results of execution so far need to be encoded (in a Transaction) and the execution to be continued needs to be passed on (in a TransactionFunction). We would probably benefit from a somewhat more sophisticated type here (this topic has been discussed before).
It should be possible to extend this model naturally to solving, where execution simply cannot take place on a node which doesn’t know of a matching intent (and where the results of execution are indeterminate, modulo the specified constraints).
Thoughts? Paging @graphomath @Lukasz @l4e21 @mariari





